
The Serial Peripheral Interface - SPI - allows digital devices to communicate using only 4 wires, additional devices can be added to the same 'bus' with the addition of only a single selection wire for each device.
There are many integrated circuits and other devices that can be controlled via SPI, this entry details a simple experiment with a MCP3201 12-bit Analogue to Digital Converter.
Background
Each end of a SPI connection is acting in one of two roles - Master or Slave. The master is responsible for initiating and controlling the communication.
The basic mode of operation is very simple: When the master wishes to initiate transfer of data:
- It sets the SS (Slave Select - often called CS - chip select) pin low to tell the slave that communication is about to start
- The master writes a bit of information onto the MOSI (Master Out Slave In) wire (sets it to 0 or 1) and the slave does the same on the MISO wire (either of these can be omitted if the data transfer is one way)
- As the master ticks the clock line SCLK it will read the value of MISO (Master In Slave Out) wire (which the Slave has written) and the slave will read the value of the MOSI wire (whether the data is sampled as the clock rises or falls depends on which mode is in operation)
- The process is repeated from (b), transferring a bit of data on each pulse of the clock until all data is transferred
The above can be implemented in software using normal I/O pins but nearly all AVR microcontrollers have hardware support, thus to transfer a byte is simply a matter of doing:
1 2 3 4 5 |
SPDR = 0x2A; // set byte to send (0x2A) // loop until SPI flag is set to signal // data has been transferred. while(!(SPIR & _bv(SPI))); // byte received from slave is now in SPDR register |
In master mode any I/O pin on the MCU can be used as chip select to tell the slave that it is being selected. In slave mode the SS pin will be lowered to indicate that this device is being selected. The I/O pins used for SPI vary across the AVR family, for example the pins for the AT90/ATmega/ATtiny are shown below along with the Arduino mappings.
| MCU/Device | SS | SCK | MOSI | MISO |
| AT90USB82/162 | PB0 | PB1 | PB2 | PB3 |
| ATmega48/88/168/328 | PB2 | PB5 | PB3 | PB4 |
| ATtiny8 | - | - | PA6 | PA5 |
| Arduino | 10 | 13 | 11 | 12 |
Setup
Although the pins may be different, setup is fairly consistent across all (as well as the spec sheets for individual MCUs AVR SPI support is described in AVR151: Setup And Use of The SPI)
SPI is configured using by setting the SPI Control Register (SPCR) with the following bits:
- Enable - (SPE) - 1 to enable SPI
- Master/Slave - (MSTR) - whether the MCU is acting as a master or slave.
- Clock Divisor - (SPR0, SPR1, SPXI2 in SPSR) indicates the frequency of the clock, represented as a divisor of the MCU clock
- SPI timing mode (CPOL, CPHA) - controls if data is output to the MISO and read on the rising or falling edge of SCK
- Data Order - (DORD) - whether MSB or LSB is transmitted first.
- Enable Interrupt (SPIE) - whether an interrupt should be raised on successful transfer of data.
To simplify setup I created a basic helper library (currently just for master mode) with a setup function that takes a timing mode, data order, interrupt, and master clock/slave spec arguments, and #defines for the values - spi.h, spi.c.
Following shows some simple SPI interaction with the MCU acting as master with a simple device.
SPI to a 12 bit A/D convertor
The picture below shows a Microchip Technology MCP3201 12 Analogue to Digital converter connected to the SPI pins of an MCU. Although most AVRs have an ADC (the AT90USB162 doesn't)- the MCP3201 offers higher precision than the 10-bits in most.
Control of the MCP3201 D/A converter is fairly straightforward and detailed in the (datasheet. When the microcontroller sets the ^CS pin to low the chip samples the input to IN+ (a potentiometer provides the input in the circuit above) sends the digital representation on Dout line as the CLK line is pulsed by the master. The MCP3201 is a read only device MOSI is not used.
From the datasheet we see the clock frequency can be a max of 1.6MHz at 5V - using a divider of 16 (assuming 16MHz Microcontroller clock) for SPI will give us a safe 1MHz clock frequency. The device is agnostic about whether operating with leading or trailing clock (can be used in any SPI timing mode). The only tricky part is that the 12 bits will be delivered in 2 bytes (most significant bit first), which will have to be recomposed into a single value - see figure 6.1 of the datasheet. The following samples the value and displays the value of the top 3 bits by lighting one of the 8 LEDs on the port B.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 |
#include <avr/io.h> #include <util/delay.h> #include <spi.h> #define SELECT_ADC PORTB &= ~(1<<PB4) #define DESELECT_ADC PORTB |= (1<<PB4) unsigned short read_adc(void) { // select ADC wait 100 microseconds then read two bytes SELECT_ADC; _delay_us(100); unsigned char one = send_spi(0xFF); _delay_us(100); unsigned char two = send_spi(0xFF); DESELECT_ADC; // 12 bits of ADC value is bottom 5 bits of first // byte and top 7 bits of second, move into 16 bit int return ((0x1F & one) << 7) | (two >> 1); } int main(void) { DDRB |= (1<<PB4); // chip select for ADC // use port D for LEDs DDRD = 0xFF; PORTD = 0x00; // make sure ADC is unselected and setup spi DESELECT_ADC; setup_spi(SPI_MODE_0, SPI_MSB, SPI_NO_INTERRUPT, SPI_MSTR_CLK16); while (1) { unsigned int num = read_adc(); PORTD = (1<< (num >> 9)); // use the top 3 bytes to turn on LED _delay_ms(1); } } |
Updated 2010/02/24:
SPI library code moved to Google Code




