MEAM.Design - ATmega32 Programming - Serial Peripheral Interface (SPI)


Overview

The ATmega32U4 has a number of serial communications modules, including one that conforms to the SPI standard. This port has full-duplex communication over three wires, can be used as either a master or a slave, has a configurable bit rate, and received packets can generate interrupts.



Configuration

To configure the SPI module, you need to disable power reduction, configure a few I/O pins, set a clock prescaler, select the SPI mode, (optionally) enable interrupts, and finally enable the SPI subsystem. It is also possible to modify many of the details of SPI operation, including the order in which bytes will be shifted between the devices, the clock polarity, and the clock phase - see the full datasheet for more information. Once enabled, the steps that you will take will depend upon whether you are configuring the device as a master or a slave. Herein we are going to assume that the ATmega32U4 is operating as the master device.


1. Disable Power Reduction

If the device has been put into a low-power mode, you must bring it back to full power to allow use of the SPI module. This is done by setting the PRSPI bit in PRR0 register.


2. Configure I/O Pins

The SPI lines consist of SCLK (Serial CLock), MOSI (Master-Out, Slave-In), MISO (Master-In, Slave-Out), and the optional SS (Slave Select). These lines are multiplexed onto the lower half of Port B. To configure as the master device, the data direction registers should be set as:

SPI function Port pin direction
MISO B3 input
MOSI B2 output
SCLK B1 output
SS B0 output (input for slave)


3. Set the clock prescaler

The SPI clock is prescaled by the SPR1 and SPR0 bits in SPCR register, where

SPCR:
SPR1
SPCR:
SPR0

 
0 0  /4
0 1  /16
1 0  /64
1 1  /128


4. Select either master or slave mode

To place the SPI module into Master mode, the MSTR bit in the SPCR register should be set to 1.

(Note - If SS is configured as an input and is driven low while MSTR is set, MSTR will be cleared, and SPIF in SPSR will become set. The user will then have to set MSTR to re-enable SPI Master mode.)


5. (Optionally) Enable interrupts

To enable interrupts from the SPI module, set the SPIE bit in the SPCR register to 1, and be sure to enable global interrupts, as explained here. Interrupts will point to SPI_STC_vect when a serial transfer is complete.


6. Enable the module

To enable SPI, set the SPE bit in the SPCR register to 1.




Usage

The SPI system operates by shifting data between two registers, one on the master and one on the slave.

1. To begin a new serial transfer, you must first pull the SS line low using clear(PORTB,0);

2. With SS low, write 8-bits of data into the SPDR register. The SPI module will recognize that you've written new data, and it will shift the packet over the slave, while simultaneously shifting in a packet from the slave.

3. Once the 8-bit transfer is complete, the SPIF bit in SPSR will go high (and if enabled, the SPI interrupt will be generated). The SPIF interrupt flag can be cleared be either executing the ISR or by first reading the SPIF bit then by either reading from or writing to SPDR.

4. If you are interested in the data returned from the slave, you can read it rom the SPDR register.

5. To transfer additional bytes, repeat steps 2-4.

6. To end transmission, return the SS line high using set(PORTB,0);




Sample Code

This sample code should enable the SPI module, transfer 0xAA to the slave, and read whatever is returned by the slave.

char outgoing = 0xAA;
char incoming;

//disable power reduction to allow SPI:
clear(PRR0,PRSPI);

// MOSI, SCLK, and SS as output:
DDRB |= (1<<DDRB2) | (1<<DDRB1) | (1<<DDRB0);

// Enable SPI, Master mode, prescaler = /128:
SPCR = (1<<SPE) | (1<<MSTR) | (1<<SPR1) | (1<<SPR0);

// send SS low to begin transmission
clear(PORTB,0);

// send a byte to the slave
SPDR = outgoing;

// wait for transmission to finish
while(!check(SPSR,SPIF));

// read the byte returned from the slave
incoming = SPDR;

// send SS high to end transmission
set(PORTB,0);