0

I'm trying to use a MS5540C (digital pressure sensor) and the sample code has this line:

 TCCR1B = (TCCR1B & 0xF8) | 1; //generates the MCLK signal

I'm a bit of a noob to the IoT but its not beyond me to check the SPI.h and see that there is indeed no declaration.

I'm using an Arduino UNO WiFi Rev 2. (Seems like its pins are quit different from an UNO) I have noted in the comments that Im using pins 32,33,34 instead of 11,12,13

I'm not 100% sure what to ask … other than Is there a replacement call/function/method that should replace that line? ..or am I missing something else totally. As it stands now...when the program runs its results are inconsistent. I'm just commenting out the faulty line to get it to compile.

Complete sketch source: Future Electronics

/*
  MS5540C Miniature Barometer Module
  This program will read your MS5440C 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.

  Uno Pins:
  MS5540 sensor attached to pins 10 - 13:
  MOSI: pin 11
  MISO: pin 12
  SCK: pin 13
  MCLK: pin 9 (or use external clock generator on 32kHz)
  CS is not in use, but might be pin 10

  ==[ addendum start]=============================
  Uno WiFi Rev2 Pins:
  MOSI: pin 32
  MISO: pin 33
  SCK: pin 34
  MCLK: pin 9 (or use external clock generator on 32kHz)
  ==[ addendum end ]=============================

  created 29 February 2012
  by MiGeRA
*/

/*
  Calibration of my sensor example ...

  Calibration word 1 = 46958
  Calibration word 2 = 65369
  Calibration word 3 = 39392
  Calibration word 4 = 45914
  c1 = 23479
  c2 = 2074
  c3 = 717
  c4 = 615
  c5 = 1021
  c6 = 25
*/

#include <SPI.h>

const int clock = 9;

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);
  delay(100);
}

void loop()
{
  Serial.println("=====================================================================");
  TCCR1B = (TCCR1B & 0xF8) | 1; //generates the MCLK signal

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

  //Calibration word 1
  unsigned int result1 = 0;
  unsigned int inbyte1 = 0;
  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
  result1 = result1 | inbyte1; //combine first and second byte of word
  Serial.print("Calibration word 1 = ");
  Serial.print(result1, HEX);
  Serial.print(" ");
  Serial.println(result1);

  resetsensor(); //resets the sensor

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

  resetsensor(); //resets the sensor

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

  resetsensor(); //resets the sensor

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

  //now we do some bitshifting to extract the calibration factors
  //out of the calibration words;
  long c1 = (result1 >> 1) & 0x7FFF;
  long c2 = ((result3 & 0x003F) << 6) | (result4 & 0x003F);
  long c3 = (result4 >> 6) & 0x03FF;
  long c4 = (result3 >> 6) & 0x03FF;
  long c5 = ((result1 & 0x0001) << 10) | ((result2 >> 6) & 0x03FF);
  long c6 = result2 & 0x003F;

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

  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;
  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
  D1 = presMSB | presLSB; //combine first and second byte of value
  Serial.print("D1 - Pressure raw = ");
  Serial.println(D1);

  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;
  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
  D2 = tempMSB | tempLSB; //combine first and second byte of value
  Serial.print("D2 - Temperature raw = ");
  Serial.println(D2); //voila!

  //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) + 20224;
  const long dT = D2 - UT1;
  const long TEMP = 200 + ((dT * (c6 + 50)) >> 10);
  const long OFF  = (c2 * 4) + (((c4 - 512) * dT) >> 12);
  const long SENS = c1 + ((c3 * dT) >> 10) + 24576;
  const long X = (SENS * (D1 - 7168) >> 14) - OFF;
  long PCOMP = ((X * 10) >> 5) + 2500;
  float TEMPREAL = TEMP / 10;
  float PCOMPHG = PCOMP * 750.06 / 10000; // mbar*10 -> mmHg === ((mbar/10)/1000)*750/06

  /*
    Serial.print("UT1 = ");
    Serial.println(UT1);
    Serial.print("dT = ");
    Serial.println(dT);
    Serial.print("TEMP = ");
    Serial.println(TEMP);
    Serial.print("OFFP = ");
    Serial.println(OFF);
    Serial.print("SENS = ");
    Serial.println(SENS);
    Serial.print("X = ");
    Serial.println(X);
  */

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

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

  //2-nd order compensation only for T < 20°C or T > 45°C

  long T2 = 0;
  float P2 = 0;

  if (TEMP < 200)
  {
    T2 = (11 * (c6 + 24) * (200 - TEMP) * (200 - TEMP) ) >> 20;
    P2 = (3 * T2 * (PCOMP - 3500) ) >> 14;
  }
  else if (TEMP > 450)
  {
    T2 = (3 * (c6 + 24) * (450 - TEMP) * (450 - TEMP) ) >> 20;
    P2 = (T2 * (PCOMP - 10000) ) >> 13;
  }

  if ((TEMP < 200) || (TEMP > 450))
  {
    const float TEMP2 = TEMP - T2;
    const float PCOMP2 = PCOMP - P2;

    float TEMPREAL2 = TEMP2 / 10;
    float PCOMPHG2 = PCOMP2 * 750.06 / 10000; // mbar*10 -> mmHg === ((mbar/10)/1000)*750/06

    Serial.print("2-nd Real Temperature in C = ");
    Serial.println(TEMPREAL2);

    Serial.print("2-nd Compensated pressure in mbar = ");
    Serial.println(PCOMP2);
    Serial.print("2-nd Compensated pressure in mmHg = ");
    Serial.println(PCOMPHG2);
  }

  delay(5000);
}

void resetsensor() //this function keeps the sketch a little shorter
{
  SPI.setDataMode(SPI_MODE0);
  SPI.transfer(0x15);
  SPI.transfer(0x55);
  SPI.transfer(0x40);
}
2
  • Haave you written this code yourself, or did you copy parts of it somewhere? If the least, plase link to that source
    – chrisl
    Oct 3, 2019 at 19:22
  • @chrisl I added the source above. If you copy the text..take note of a char that is the 'f" and the 'l' combined!...ie in "float"...just retype it. Oct 3, 2019 at 19:30

1 Answer 1

3

While writing this answer, I found out about the register emulation of the Arduino Uno Wifi. The Uno Wifi is a different chip, than the Uno, thus you have different registers. But the Uno Wifi was thought as a drop in replacement, so register emulation maps the old register names to the new registers, so that you can basically simply reuse the old code for the Uno. In my Arduino IDE (v1.8.9) I had a buildin Uno Wifi board, which seems to have the register emulation enabled without configuration option (I could compile your sketch even for the Uno Wifi). Through the board manager I installed the "Arduino megaAVR Boards" core, that contains the Arduino Uno Wifi rev2. If you choose this board in the board selection, you get the option to select the register emulation. For old code you need the emulation for ATmega328. With that, your old code should compile.

This makes the rest of my answer unnecessary, but since I already spend some time writing it, I will leave it in the answer for information and for the case, that you don't want to use the register emulation.


You get the error, because the sketch, that you copied is made for the Arduino Uno, not the Arduino Uno Wifi. While the first uses an ATmega328P chip, the last is build with an ATmega4809 (you can find this information here at the Arduino.cc store). These are different chips, thus you cannot simply reuse code that is made for the Uno on the Uno Wifi.

TCCR1B is the control register B of Timer 1. This name is specific to the ATmega328P chip. The ATmega4809 has even more timers than the 328P, but they seem to get configured via different registers. Refer to the datasheets on the product site of the ATmega4809. The timer hardware and it's registers are described in the more general megaAVR 0 series family datasheet. At the first glance it seems, that not only the register description is different from the 328P, but also the positions of the different configurations. This means, that every code, that uses direct register manipulations for the timer, must be rewritten for the 4809.

Fortunately, I see only 1 line here, that does this: The line, that generated the compiler error. (The SPI library very likely already has fitting definitions inside for the 4809). You have the line

TCCR1B = (TCCR1B & 0xF8) | 1;

That clears the 3 least significant bits (leaving the rest as it was) and setting the least significant bit to 1. According to chapter 16.11.2 of the 328P's datasheet (description of the TCCR1B register), these 3 bits are the clock selection. A bit code of 001 (only least significant bit set) means timer on, but with no prescaling.

To be honest, I'm not sure, if this line really does something, since directly after that, analogWrite() is called and I thought this function would overwrite the timer settings. But other posts (like this one) suggest, that it at least does not overwrite the clock selection.

I don't know, which timer get's used for outputting PWM to that pin, but for now, I will assume the first timer of type B. If you look into the families datasheet (as linked above) the clock source of the second timer (TCB) is configured with the register TCB0.CTRLA (see chapter 20.5.1). Bits 1 and 2 are configuring the prescaler (binary 00 for no prescaler), bit 0 enables the timer. So you can again write binary 001 to this register, leaving the rest untouched. So I suggest, you use this line instead of the old one

TCB0.CTRLA = (TCB0.CTRLA & 0xF8) | 1;

This should do the same with the second timer, that was done in the original sketch. If the first timer is used for this, you would have to change TCB0 to TCA0.SINGLE (I really don't know, why this SINGLE is needed) and change 0xF8 to 0xF0, since TCA has 3 bits for selecting the clock source.

3
  • I might be a little over my head. I do have the right board in boardmanager. I had also tried changing the emulation. I still get the error! My initial thoughts were the preprocessor was boogering up the code. The calibration words keep changing. I can see now Im going to have to get a grip on registers,clocks..etc..etc. Oct 3, 2019 at 23:22
  • I think the emulation doesn't cover timers
    – Juraj
    Oct 4, 2019 at 7:32
  • @Juraj But the old code compiles for me only with register emulation. Doesn't that mean, that it also does the emulation for the timer registers?
    – chrisl
    Oct 4, 2019 at 7:56

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

Not the answer you're looking for? Browse other questions tagged or ask your own question.