SCP1000 pressure sensor notes

From Helpful
Jump to navigation Jump to search
This article/section is a stub — probably a pile of half-sorted notes and is probably a first version, is not well-checked, so may have incorrect bits. (Feel free to ignore, or tell me)

Some specs

  • 30kPa - 120kPa pressure range (≈ 0.3 atm to ~1.2 atm)
  • ~1.5Pa resolution (according to datasheet; may vary a little depending on use. I got jitter of a few dozen Pascal in all modes (but this may be my wiring - my first breadboarded tests jittered a lot more and I never quite figured out why)
  • also exposes the temperature that it uses for correction (in 0.05°C steps)
  • wants 3.3V (supply and communication)
  • Two variants: one that speaks I2C (at max 400kHz) and one that speaks SPI (at max 500kHz).
VTI uses D01 to mark the SPI one, and D11 to mark the I2C one
  • power consumption quoted as:
    • 0.2 µA in powerdown mode (while you hold its PD pin high)
    • ~1µA in standby (e.g. while waiting to be triggered, and in non-sampling mode)
    • ~3.5µA for periodic measurements (~1/sec)
    • ~25µA for continuous high-res or high-speed measurements
    • 25µA down to nearly 1µA for triggered, depending on speed of triggering

Notes on operation

On timing:

  • Startup (after first powered, after powerdown, and after reset) takes up to 150ms:
    • 60ms is basically a given wait time
    • ...after which you can either wait for STATUS's STARTUP bit, or just wait approx 90ms
  • In SPI, wait for ≥100ns after you assert slave select (more than that may be prudent)


The modes of operation (write into OPERATION):

  • (0x0A) continuous measurements, high resolution mode (17 bits, refreshing at ~1.8Hz, consuming ~25µA)
  • (0x09) continuous measurements, high speed (15 bits, refreshing at ~9Hz, consuming ~25µA)
  • (0x0B) periodic measurements (15 bits, refreshing at ~1Hz, consuming ~3.5µA)
  • (0x03) triggered (lower power, stands by at ~1µA and active time depends on trigger speed, e.g. ~25µA for ~2/sec and ~3µA for ~1/sec)
  • (0x00) non-sampling mode (~1µA; also useful as low(-ish) power state, also necessary between mode switches)

I'm guessing the speed versus resolution is just how much it supersamples. Note that the pressure value is always retrieved as a 19-bit int (always in the same scale), that the 15/17 bits seems to be an indication of whether the lowest-significance bits should be considered noise.


While the SCP1000 is an SPI slave, DRDY (both a pin and an internal register bit) is raised when the module has a new sample ready. You could use it to signal (or interrupt) the SPI master to service the SPC1000. Since the DRDY state is in a register, you don't need to use DRDY wire to trigger the master, but it does mean you'll service it faster and don't need to poll the sensor a lot.

Fast-enough servicing matters because in the continuous sampling modes, the chip expects you to consume the sample fairly quickly after it raises DRDY. After you read DATARD16, DRDY will go low again. If you do not read the value in time (that time depends mostly on refresh speed of the mode), STATUS[RTERR] (bit 4) will become 1, and you have to read DATARD16 to clear this -- and that read may give nonsense data).


When you cannot guarantee that you'll read out the value fast enough (e.g. on microcontrollers that can be unpredictably busy), look at triggered mode: it only requires that you read the data before the next time you trigger(verify). To use triggering, set the triggered mode, and then either pulse on the TRIG pin (pulse length at least 200ns) or write 0x0C in the OPERATION register (0x03). The sensor needs some time doing the actual sample before it raises DRDY (apparently at least 140ms in the 15-bit mode and at least 500ms in the 17-bit mode). To select the resolution you set 0x05 in CFG for 17 bits (the default), or 0x0D for 15 bits, before triggering.


SPC1000's registers (from the datasheet)

The more interesting registers:

  • (0x03) OPERATION - set the mode here. When switching between modes you should write 0x00 first to disable sampling, then set the mode you want. Check for readiness in the status register - or just wait for ~50ms(verify))
  • (0x1F in SPI, 0x7F in TWI) DATARD8 - the 3 most significant bits [2:0] of the 19-bit pressure value
  • (0x20 in SPI, 0x80 in TWI) DATARD16 - the 16 least significant bits of the 19-bit pressure data. Note that reading this specific register has other effects
  • (0x21 in SPI, 0x81 in TWI) TEMPOUT - 14 bits of temperature data in [13:0]
  • (0x07) STATUS - ASIC status
    • Bit 0 (STARTUP): 0 if startup has finished, 1 if it's still working
    • Bit 4 (RTERR): data not read fast enough after DRDY; clear by reading DATARD16
    • Bit 5 (DRDY): mirrors DRDY pin
  • (0x00) CFG, lets you set 17 bits (0x05) or 15 bits (0x0D) and only seems relevant to triggered mode

There are more, some registers do double service when doing indirect data reads/writes, and you can read/write the EEPROM, but if you need any of that you can can shoo and read the PDF.


The pressure data is an integer in the range 120000 (for 30kPa) and 480000 (for 120kPa), so divide by 4. If the raw figure is outside that range you should treat it as invalid. I've seen values around four billion.

The temperature data is a signed (two's complement) int, and you divide by 20 for degrees Celsius (so has ~.05 degrees resolution).


If you take the PD pin high, the sensor will stay powered down (consuming ~0.2 μA), and the output pins will tristate. If you take PD low to start it up, wait a bit for startup.


See also:


On an Arduino

These are primarily notes
It won't be complete in any sense.
It exists to contain fragments of useful information.

Most arduinos are 5V and the SCP1000 wants all its pins to be 3.3V (including the SPI ones), so unless you have a 3.3V Arduinos you will want to drop the voltage from 5V to ~3.3V. (It'll function on 5V, but it's probably not very good for sensor lifetime)

The easiest way, which cheats a little, is probably to add some inline resistors the lines (except MISO, as that's powered from the SCP1000 side), mostly to make the job easier for the SCP's internal clamping diodes - they can only handle so much current, and you're limiting how much they'll see.

For this and some other solutions, see [1].

You may also want to pull up slave select, though this may not be strictly necessary(verify).


Example code, using an interrupt setup (which means you free your loop() from having to poll (but make sure there's no noise on the interrupt line)):

/*
 * Reads VTI's SCP1000-D01 (SPI) 30-120kPa pressure sensor 
 * 
 * Based on some code on the arduino.cc forums by a certain Conor,
 *  generalized somewhat and adapted to follow the datasheet more, 
 *  although I'm not sure whether this needs more delays, 
 *   as e.g. in the VTI code sample.
 *
 * This version uses the sensor's DRDY pin to interrupt the Arduino 
 * (via arduino digital pin 2), which means we don't have to poll 
 * from the arduino side, and we service the sensor more immediately
 * after it's done.
 * 
 * Without that interrupt you would probably poll for the STATUS's DRDY bit: 
 *   if (scp_read_register8(SCP1000_REG_STATUS, SPI_SLAVESELECT)&B00100000)
 *     service_scp1000();
 * I suggest only doing this with triggered mode, to avoid the whole RTERR issue.
 * 
 * You can simplify this code a little (it was stripped from a two-SCP1000 test)
 */

// MOSI, MISO and SCLK are fixed if you use the AVR's hardware SPI
//  but you still need to declare them as input/output pins in setup()
#define SPI_SLAVESELECT  10
#define SPI_MOSI         11  
#define SPI_MISO         12  
#define SPI_CLOCK        13

//The SCP1000 register addresses that we use
#define SCP1000_REG_RSTR      0x06   //reset
#define SCP1000_REG_OPERATION 0x03   //Mode of operation
#define SCP1000_REG_STATUS    0x07   //ASIC Status
#define SCP1000_REG_DATARD8   0x1F   //most significant 3 bits of 19-bit pressure
#define SCP1000_REG_DATARD16  0x20   //least significant 16 bits of 19-bit pressure
#define SCP1000_REG_TEMPOUT   0x21   //14 bit temperature
#define SCP1000_REG_OPSTATUS  0x04   //Operation Status

#define SCP1000_OPERATION_HIGHRES   0x0A   // 17 bits, refreshing at ~1.8Hz, consuming ~25uA
#define SCP1000_OPERATION_HIGHSPEED 0x09   // 15 bits, refreshing at ~9Hz, consuming ~25uA
#define SCP1000_OPERATION_LOWPOWER  0x0B   // 15 bits, refreshing at ~1Hz, consuming ~3.5uA
#define SCP1000_OPERATION_TRIGGERED 0x0C   // 15 bits (~140ms) or 17bits (~500ms) depending on CFG register,
                                           // stands by at ~1μA and uses 1-25uA depending on trigger speed



//Volatile because they are updated from the interrupt function
volatile boolean new_sample;
volatile byte temp_status;
volatile int temperature_raw;
volatile signed int signed_temperature;
volatile unsigned long pressure_raw;

unsigned long last_triggered;


void setup() {
  Serial.begin(9600); // For monitoring

  // SPI init
  byte clr; //dummy variable
  pinMode(SPI_MOSI,        OUTPUT); 
  pinMode(SPI_MISO,        INPUT );
  pinMode(SPI_CLOCK,       OUTPUT);
  pinMode(SPI_SLAVESELECT, OUTPUT);
  digitalWrite(SPI_SLAVESELECT, HIGH); // HIGH: do not select
  digitalWrite(SPI_CLOCK, HIGH);
  SPCR = B01010011; // MPIE=0 (no interrupt enable)
                    //  SPE=1 (enable SPI)
                    // DORD=0 (MSB first)
                    // MSTR=1 (AVR is master)
                    // CPOL=0 (clock idle when low)
                    // CPHA=0 (samples on rising edge)
                    // SPR1=1 & SPR0=1 (250kHz)
  clr=SPSR; // clear status register (by just reading it)
  clr=SPDR; // clear data register (by just reading it)

  //SCP1000 Init: Reset, wait for startup (takes ~60ms because we explicitly wait that long), set sampling mode
  scp_write_register(SCP1000_REG_RSTR, 0x01, SPI_SLAVESELECT);
  delay(60);
  while (scp_read_register8(SCP1000_REG_STATUS, SPI_SLAVESELECT)&B00000001) 
    delay(10);
  //presumably the 50ms wait after setting 0x00 mode doesn't apply when we have just reset
  //scp_write_register(SCP1000_REG_OPERATION, SCP1000_OPERATION_HIGHRES,   SPI_SLAVESELECT);
  //scp_write_register(SCP1000_REG_OPERATION, SCP1000_OPERATION_HIGHSPEED, SPI_SLAVESELECT);
  //This examples uses (SPI-based) triggering, so the previous two lines (continuous modes) are commented out

  //Use a rising edge on Arduino pin 2 (connected to the SCP1000's DRDY pin) 
  // to service the sensor via an interrupt.
  pinMode(2,INPUT);
  attachInterrupt(0, service_scp1000, RISING); // interrupt 0: arduino digital pin 2

  new_sample=false;
}


void loop() {

   //when you want an occasional, triggered sample:
   // (if you set a continuous mode in setup, you don't want this part)
   if (abs(millis()-last_triggered)>5000) { // For example every five seconds
      scp_write_register(SCP1000_REG_OPERATION, SCP1000_OPERATION_TRIGGERED, SPI_SLAVESELECT);
      last_triggered=millis();
   } else {
      // Twiddle our thumbs visibly.
      delay(10);
      Serial.write('.');
   }

   
   // When the interrupt function marks that a new sample is available, print it.
   if (new_sample) {
      new_sample=false;
      Serial.print("\nTemperature: ");
      Serial.print(signed_temperature/20.);
      Serial.print(" degrees Celsius   (raw: ");
      Serial.print(temperature_raw);
      Serial.print(")\nPressure:    ");
      Serial.print(pressure_raw/400.);
      Serial.print(" millibar         (raw: ");
      Serial.print(pressure_raw);
      Serial.print(")\n\n");
   }
}


void service_scp1000() { // Reads the sensor, sets mostly-raw values in global volatile variables   
    // Theoretically we won't miss a DRDY when it interrupts the uC, but it can't hurt to check
    temp_status = scp_read_register8(SCP1000_REG_STATUS, SPI_SLAVESELECT);
    if (temp_status&B00010000) { 
      //if either RTERR was set, clear it by reading data and skip real output
      scp_read_register16(SCP1000_REG_DATARD16, SPI_SLAVESELECT);  
      ////check that that worked:
      //tstat2 = scp_read_register8(SCP1000_REG_STATUS, SPI_SLAVESELECT1);
      //if (tstat2&B00010000) Serial.print("RTERR cleared            "); 
      //else                  Serial.print("RTERR still there - huh? "); 
      //Serial.print("\n");
      // We give up on this sample and wait for the next.
 
    } else { // No error, go ahead and read.
      temperature_raw = scp_read_register16(SCP1000_REG_TEMPOUT, SPI_SLAVESELECT);
      // This should deal with negative temperatures -- and some of this is probably redundant
      if (temperature_raw&0x2000)  signed_temperature=temperature_raw|0xC000;
      else                         signed_temperature=temperature_raw&0x1FFF; //mask probably unnecessary

      pressure_raw = ((unsigned long)scp_read_register8(SCP1000_REG_DATARD8, SPI_SLAVESELECT))<<16;
      pressure_raw = pressure_raw|scp_read_register16(SCP1000_REG_DATARD16, SPI_SLAVESELECT);
      //Note that if this raw figure is outside the range 120000-480000, 
      // you should consider it invalid (and it may be wildly so)
 
      new_sample=true;
    }

}


/********************** SPI helper functions ***************/
char spi_transfer(volatile char data=0x00) { //0x00 is dummy data in case you want to receive
    SPDR = data;                     // triggers send
    while (!(SPSR & (1<<SPIF))) {};  // wait for transmission end
    return SPDR;                     // return the received byte
}


/* Asks for pin because this was written to test two SPC1000s on the same bus
 *
 * The SCP wants its addresses shifted <<2 
 *  with the new bit 0 always 0, 
 *           and bit 1 controls read(0)/write(1)
 */
char scp_read_register8(char register_address, byte select_pin) {
    char in_byte;
    register_address = register_address<<2;
    digitalWrite(select_pin,LOW); //Select SPI Device
    spi_transfer(register_address); //Write byte to device
    in_byte = spi_transfer(); 
    digitalWrite(select_pin,HIGH);
    return in_byte;
}


unsigned int scp_read_register16(char register_address, byte select_pin) {
    byte in_byte1,in_byte2;
    unsigned int in_word;
    register_address = register_address<<2;
    digitalWrite(select_pin,LOW); //Select SPI Device
    spi_transfer(register_address);   
    in_byte1 = spi_transfer();    
    in_byte2 = spi_transfer();
    digitalWrite(select_pin,HIGH);
    in_word = (in_byte1<<8)|in_byte2;
    return in_word;
}


void scp_write_register(char register_address, char register_value, byte select_pin) {
    register_address = (register_address<<2)|B00000010;
    digitalWrite(select_pin,LOW); //Select SPI device
    spi_transfer(register_address); //Send register location
    spi_transfer(register_value); //Send value to record into register
    digitalWrite(select_pin,HIGH);
}

Note that for continuous sampling and two sensors, waiting for one DRDY and then the other will mean your reads will be out of sync and you'll eventually not service a DRDY fast enough. You can choose to miss one sample and rely on checking RTERR and clearing with a read, but there are various cleverer setups.