SPI (Serial Peripheral Interface)
The SPI (Serial Peripheral Interface) is a method of interconnecting electronic devices. This can be two microcontrollers or a microcontroller and a peripheral device. The link features are:
- 3 Wire + 1 interface
- 2 data lines one is output and the other is input.
- A clock is used.
- Chip select used for device addressing.
- Master-Slave operation, the master device controls the clock.
- Normally uses commands.
- Bi-directional – full duplex communication possible.
It is essentially a 3 wire + 1 interface with a concept of master and slave. The master does the clocking whilst the slave responds to the clock. The interface is unfortunately compromised by the lack of standards which means that the clock can be either positive or negative, the data in and out lines can also have differences and the data can be either MSB (most significant bit) first or LSB first. This all leads to a rather complex register configuration on the PIC32, however so far all the devices I have tested seem to work with a single configuration so that will be used.
The two main communication connections are:
MOSI (master out slave in)
MISO (master in slave out)
In common with RS232, the output of one goes into the input of another (crossed over). One interesting aspect of this interface is that whilst data is being clocked in, it is also being clocked out at the same time. If just read is required then 0xff is normally clocked in order to get the data out.
There does not seem to be as many SPI devices as there are i2c devices. SPI tends to be reserved for devices that require high speed namely, SC Cards, Network interfaces and radio links. There are a couple of exceptions and one of them is presented here.
The Digital Potentiometer
Digital Pot (variable resistor), these are sometimes used as digital to analogue convertors and can be very useful for real world interfacing. The one used here is a MCP41xxx variety that has just the one potentiometer but others are available with two pots on board.

Port E bit 6 is used as the chip select and this must be taken low before any reading or writing of the spi bus. The wiring as shown will output a voltage in 256 steps from near 0V to near 5V. To make things simple, the same interface (spi2) is used that is used for the SC Card and so no initialisation of the interface is required.
// this device works at 20MHz
// using spi2
// cs is RE6
function spi_init
portb "etc" 6
portb "eos" 6
endf
// value from 0 to 255 will set resistance between
// 0 and 10k
function pot(value)
portb "eoc" 6 // cs low
spi2(0x11) // command byte
spi2(value)
portb "eos" 6 // cs high
endf
The complete program interface is show here along with comments, spi_init should be run first as this sets up the port used for the chip select. As this is a simple device the keyword ‘spi2’ is used to feed bytes to the device. This particular device requires 16 bits, the first 8 bits is a command (0x11), which means write to pot 0, the second 8 bits is the value sent to the pot.
As this device only has one pot and is not capable of read, this is the only command that is really acceptable. SPI1 can also be used if required but this will require initialising:
// chip select
portb "etc" 6
portb "eos" 6
// set up spi1 which is also UART1
spiinit 1 2000000
endf
Setting up SPI1 is very simple but remember that it uses the same interface as UART1 and so this UART cannot be used if SPI1 is going to be used.
SPI EEPROM
Another useful SPI device, and low cost, is the serial EEPROM. The device chosen for this example is the M95xx or the AT25. Different manufactures have different part numbers. There may also be slight variations in commands required so reference to the part datasheet is essential. The examples given are for the M951.

This is a small 8 pin DIL as shown. For this application both the HOLD and W lines are tied to the +5V rail.

Chip select is as before using port F6 which can be obtained on connector J3-2. SPI1 shares the port lines with UART1 and so initialising the SPI will disable this UART. The main communication through the USB uses UART2 so this will not be affected.
// SPI1
function spi_read(address, buf%, length)
dim hi, lo
spi_cs 0 // cs low
hi = and(rshift(address,8),0xff)
lo = and(address,0xff)
spi1(0x3) // read instruction
spi1(hi)
spi1(lo) // send address
spiread 1 buf% length
spi_cs 1 // cs high
endf
// write a string to an EEPROM connected to
// SPI1
function spi_write(address, s$)
dim hi, lo
hi = and(rshift(address,8),0xff)
lo = and(address,0xff)
spi_w 6 // write enable
spi_cs 0 // cs low
spi1(2) // write instruction
spi1(hi) spi1(lo) // send address
spiwrite 1 adr(s$) len(s$)
while spi_ready = 1 : wend // wait for write to finish
spi_cs 1
endf
Just the read and write routines are shown here and these are intended for reading and writing strings. A buffer address must be supplied to the read routine, supplying a string and then presenting its address to ‘spiread’ will not work because the parameter variables, the ones in brackets after the function name, are local to that function. For example:
function spi_read(address, s$, length)
.
.
.
spiread 1 adr(s$) length
.
.
The string s$ is local and so although s$ will be filled with the contents, the string passed into the function will not. Remember also that when using page write as above, these devices usually have a limit to the page, this device in particular is 32 bytes.