SPI

From chipKIT
Jump to: navigation, search
SPI
Quick Look
Hardware (External hardware)
Include SPI.h

The SPI library allows the chipKIT to communicate with devices using the Serial Peripheral Interface (SPI) Protocol.

Detailed Introduction

The SPI library has been ported to chipKIT from the Arduino SPI library. See SoftSPI and DSPI for alternative SPI libraries which allow for bit-banging and use of other hardware SPI peripherals respectively.

The SPI protocol is a synchronous serial data protocol which contains one master (chipKIT) and one or more slave devices. It's designed for fast communication over short distances such as between two ICs on a PCB.

Wiring

SPI Wiring Diagram

This SPI Library assumes that your slave devices are connected to the MOSI (SDO), MISO (SDI), and SCK pins which are pre-defined in the Board_Def.h file for the board you are using. These 3 pins are common to all devices. Since the pin are already defined in the board definition file you don't need to configure them in code. However, there is a fourth pin called SS for Slave Select. One slave select pin is defined in the Board_Def.h file. However, this SPI library allows you to override that pin and use another pin instead. Most if not all chipKIT boards will phyiscally identify the 3 common pins by their SDO, SDI, and SCK names on the silkscreen. Consult your boards user manual if you are unable to locate them.


SPI Pin Definitions

Label Name Definition
MOSI (SDO) Master Out Slave In Used for sending data from the master to the slave
MISO (SDI) Master In Slave Out Used for sending data from the slave to the master
SCK Serial Clock Syncronizes data transmission.
SS Slave Select A pin to signal to a chip that the master is trying to communcate with it.


Introductory Programs

Barometric Pressure Sensor Display

Prints the output of a Barometric Pressure Sensor to the Sserial Monitor.

/*
 SCP1000 Barometric Pressure Sensor Display
 
 Shows the output of a Barometric Pressure Sensor on a
 Uses the SPI library. For details on the sensor, see:
 http://www.sparkfun.com/commerce/product_info.php?products_id=8161
 http://www.vti.fi/en/support/obsolete_products/pressure_sensors/
 
 This sketch adapted from Nathan Seidle's SCP1000 example for PIC:
 http://www.sparkfun.com/datasheets/Sensors/SCP1000-Testing.zip
 
 Circuit:
 SCP1000 sensor attached to pins 6, 7, 10 - 13:
 DRDY: pin 6
 CSB: pin 7
 MOSI: pin 11
 MISO: pin 12
 SCK: pin 13
 
 created 31 July 2010
 modified 14 August 2010
 by Tom Igoe
 */
 
// the sensor communicates using SPI, so include the library:
#include <SPI.h>
 
//Sensor's memory register addresses:
const int PRESSURE = 0x1F;      //3 most significant bits of pressure
const int PRESSURE_LSB = 0x20;  //16 least significant bits of pressure
const int TEMPERATURE = 0x21;   //16 bit temperature reading
const byte READ = 0b11111100;     // SCP1000's read command
const byte WRITE = 0b00000010;   // SCP1000's write command
 
// pins used for the connection with the sensor
// the other you need are controlled by the SPI library):
const int dataReadyPin = 6;
const int chipSelectPin = 7;
 
void setup() {
  Serial.begin(9600);
 
  // start the SPI library:
  SPI.begin();
 
  // initalize the  data ready and chip select pins:
  pinMode(dataReadyPin, INPUT);
  pinMode(chipSelectPin, OUTPUT);
 
  //Configure SCP1000 for low noise configuration:
  writeRegister(0x02, 0x2D);
  writeRegister(0x01, 0x03);
  writeRegister(0x03, 0x02);
  // give the sensor time to set up:
  delay(100);
}
 
void loop() {
  //Select High Resolution Mode
  writeRegister(0x03, 0x0A);
 
  // don't do anything until the data ready pin is high:
  if (digitalRead(dataReadyPin) == HIGH) {
    //Read the temperature data
    int tempData = readRegister(0x21, 2);
 
    // convert the temperature to celsius and display it:
    float realTemp = (float)tempData / 20.0;
    Serial.print("Temp[C]=");
    Serial.print(realTemp);
 
 
    //Read the pressure data highest 3 bits:
    byte  pressure_data_high = readRegister(0x1F, 1);
    pressure_data_high &= 0b00000111; //you only needs bits 2 to 0
 
    //Read the pressure data lower 16 bits:
    unsigned int pressure_data_low = readRegister(0x20, 2);
    //combine the two parts into one 19-bit number:
    long pressure = ((pressure_data_high << 16) | pressure_data_low) / 4;
 
    // display the temperature:
    Serial.println("\tPressure [Pa]=" + String(pressure));
  }
}
 
//Read from or write to register from the SCP1000:
unsigned int readRegister(byte thisRegister, int bytesToRead ) {
  byte inByte = 0;           // incoming byte from the SPI
  unsigned int result = 0;   // result to return
  Serial.print(thisRegister, BIN);
  Serial.print("\t");
  // SCP1000 expects the register name in the upper 6 bits
  // of the byte. So shift the bits left by two bits:
  thisRegister = thisRegister << 2;
  // now combine the address and the command into one byte
  byte dataToSend = thisRegister & READ;
  Serial.println(thisRegister, BIN);
  // take the chip select low to select the device:
  digitalWrite(chipSelectPin, LOW);
  // send the device the register you want to read:
  SPI.transfer(dataToSend);
  // send a value of 0 to read the first byte returned:
  result = SPI.transfer(0x00);
  // decrement the number of bytes left to read:
  bytesToRead--;
  // if you still have another byte to read:
  if (bytesToRead > 0) {
    // shift the first byte left, then get the second byte:
    result = result << 8;
    inByte = SPI.transfer(0x00);
    // combine the byte you just got with the previous one:
    result = result | inByte;
    // decrement the number of bytes left to read:
    bytesToRead--;
  }
  // take the chip select high to de-select:
  digitalWrite(chipSelectPin, HIGH);
  // return the result:
  return(result);
}
 
 
//Sends a write command to SCP1000
 
void writeRegister(byte thisRegister, byte thisValue) {
 
  // SCP1000 expects the register address in the upper 6 bits
  // of the byte. So shift the bits left by two bits:
  thisRegister = thisRegister << 2;
  // now combine the register address and the command into one byte:
  byte dataToSend = thisRegister | WRITE;
 
  // take the chip select low to select the device:
  digitalWrite(chipSelectPin, LOW);
 
  SPI.transfer(dataToSend); //Send register location
  SPI.transfer(thisValue);  //Send value to record into register
 
  // take the chip select high to de-select:
  digitalWrite(chipSelectPin, HIGH);
}

Digital Pot Control

This example controls an Analog Devices AD5206 digital potentiometer.

/*
  Digital Pot Control
 
  This example controls an Analog Devices AD5206 digital potentiometer.
  The AD5206 has 6 potentiometer channels. Each channel's pins are labeled
  A - connect this to voltage
  W - this is the pot's wiper, which changes when you set it
  B - connect this to ground.
 
 The AD5206 is SPI-compatible,and to command it, you send two bytes,
 one with the channel number (0 - 5) and one with the resistance value for the
 channel (0 - 255).
 
 The circuit:
  * All A pins  of AD5206 connected to +5V
  * All B pins of AD5206 connected to ground
  * An LED and a 220-ohm resisor in series connected from each W pin to ground
  * CS - to digital pin 10  (SS pin)
  * SDI - to digital pin 11 (MOSI pin)
  * CLK - to digital pin 13 (SCK pin)
 
 created 10 Aug 2010
 by Tom Igoe
 
 Thanks to Heather Dewey-Hagborg for the original tutorial, 2005
 
*/
 
 
// inslude the SPI library:
#include <SPI.h>
 
 
// set pin 10 as the slave select for the digital pot:
const int slaveSelectPin = 10;
 
void setup() {
  // set the slaveSelectPin as an output:
  pinMode (slaveSelectPin, OUTPUT);
  // initialize SPI:
  SPI.begin();
}
 
void loop() {
  // go through the six channels of the digital pot:
  for (int channel = 0; channel < 6; channel++) {
    // change the resistance on this channel from min to max:
    for (int level = 0; level < 255; level++) {
      digitalPotWrite(channel, level);
      delay(10);
    }
    // wait a second at the top:
    delay(100);
    // change the resistance on this channel from max to min:
    for (int level = 0; level < 255; level++) {
      digitalPotWrite(channel, 255 - level);
      delay(10);
    }
  }
 
}
 
void digitalPotWrite(int address, int value) {
  // take the SS pin low to select the chip:
  digitalWrite(slaveSelectPin, LOW);
  //  send in the address and value via SPI:
  SPI.transfer(address);
  SPI.transfer(value);
  // take the SS pin high to de-select the chip:
  digitalWrite(slaveSelectPin, HIGH);
}

Full library usage

SPISettings

The SPISettings class is used to create an SPISettings object which is passed to the beginTransaction fuction. You use SPISettings to configure your SPI hardware peripherals clock speed, bit order, and mode (polarity, phase, and clock edge).

Constants


Flags for Bit Order

Name Value
LSBFIRST 0
MSBFIRST 1

Flags for SPI Modes

Name Value Clock Polarity
(CPOL/CKP)
Clock Phase
(CPHA)
Clock Edge
(CKE/NCPHA)
SPI_MODE0 0x00 0 0 1
SPI_MODE1 0x04 0 1 0
SPI_MODE2 0x08 1 0 1
SPI_MODE3 0x0C 1 1 0

Flags for Clock Dividers

Name Value
SPI_CLOCK_DIV4 0x00
SPI_CLOCK_DIV16 0x01
SPI_CLOCK_DIV64 0x02
SPI_CLOCK_DIV128 0x03
SPI_CLOCK_DIV2 0x04
SPI_CLOCK_DIV8 0x05
SPI_CLOCK_DIV32 0x06


Constructors


SPISettings()

 
SPISettings()

Defaults to Clock=4000000, bitOrder=MSBFIRST, dataMode=SPI_MODE0


SPISettings(uint32_t clock, uint8_t bitOrder, uint8_t dataMode)

 
SPISettings(uint32_t clock, uint8_t bitOrder, uint8_t dataMode)

- clock speed is not critical in SPI. However, you should not exceed the max speed of your slave devices.

- bitOrder as defined in the constants section sets MSB or LSB first.

- dataMode configured data polarity as defined in the constants section.


Public Functions

None


SPIClass

Constructors

SPIClass(uint32_t base))

 
SPIClass(uint32_t base);


SPIClass(uint32_t base, int pinMI, int pinMO, ppsFunctionType ppsMI, ppsFunctionType ppsMO)

 
SPIClass(uint32_t base, int pinMI, int pinMO, ppsFunctionType ppsMI, ppsFunctionType ppsMO);


Public Functions


begin()

 
void begin();

Initialize the SPI library


usingInterrupt(uint8_t interruptNumber)

 
void usingInterrupt(uint8_t interruptNumber);

If SPI is used from within an interrupt, this function registers that interrupt with the SPI library, so beginTransaction() can prevent conflicts. The input interruptNumber is the number used with attachInterrupt. If SPI is used from a different interrupt (eg, a timer), interruptNumber should be 255.


notUsingInterrupt(uint8_t interruptNumber)

 
void notUsingInterrupt(uint8_t interruptNumber);

And this does the opposite.


beginTransaction(SPISettings settings)

 
void beginTransaction(SPISettings settings)

Before using SPI.transfer() or asserting chip select pins, this function is used to gain exclusive access to the SPI bus and configure the correct settings.


transfer(uint8_t data)

 
uint8_t transfer(uint8_t data)

Write to the SPI bus (MOSI pin) and also receive (MISO pin)


transfer(void *buf, size_t count)

 
void transfer(void *buf, size_t count)

Write to the SPI bus (MOSI pin) and also receive (MISO pin)


transfer16(uint16_t data)

 
uint16_t transfer16(uint16_t data)

Write to the SPI bus (MOSI pin) and also receive (MISO pin)


endTransaction(void)

 
void endTransaction(void)

After performing a group of transfers and releasing the chip select signal, this function allows others to access the SPI bus.


end()

 
void end();

Disable the SPI bus


setBitOrder(uint8_t bitOrder)

 
void setBitOrder(uint8_t bitOrder)

This function is deprecated. New applications should use beginTransaction() to configure SPI settings.


setDataMode(uint8_t dataMode)

 
void setDataMode(uint8_t dataMode)

This function is deprecated. New applications should use beginTransaction() to configure SPI settings.


setClockDivider(uint8_t clockDiv)

 
void setClockDivider(uint8_t clockDiv)

This function is deprecated. New applications should use beginTransaction() to configure SPI settings.


External Links