Pages Menu
TwitterRssFacebook
Categories Menu

Posted by on Nov 26, 2013 in Atmel AVR, Microcontrollers | 81 comments

The SPI of the AVR

The SPI of the AVR

Continuing with the series of tutorials on Serial Communication, here is another one, and much awaited, the Serial Peripheral Interface (SPI) of AVR! Before proceeding ahead, I would suggest you to read Mayank’s tutorial on the basics of SPI.

Contents

Serial Peripheral Interface (SPI) – Basics Revisited

Here we will discuss some basics of Serial Peripheral Interface (SPI, pronounced spy or ess-pee-eye). Mayank has already dealt with the basics of SPI and SPI bus transactions in the previous tutorial, but I will go over some of the nitty-gritties here again.

Serial Peripheral Interfacing is one of the most used serial communication protocols, and very simple to use! As a matter of fact, I find this one much simpler than USART! ;)

Since SPI has been accepted as a de facto standard, it is available in almost all architectures, including 8051, x86, ARM, PIC, AVR, MSP etc., and is thus widely used. This means that there shouldn’t be any portability issues and you can connect devices of two different architectures together as well!

So here are the most popular applications of SPI:

  1. Wired transmission of data (though the first preference is mostly USART, but SPI can be used when we are using multiple slave or master systems, as addressing is much simpler in SPI).
  2. Wireless transmissions through Zigbee, 2.4GHz etc.
  3. Programming your AVR chips (Yes! They are programmed through the SPI! You’ll would have read about it in Mayank’s Post on SPI).
  4. It is also used to talk to various peripherals – like sensors, memory devices, real time clocks, communication protocols like Ethernet, etc.

Advantages of SPI

SPI uses 4 pins for communications (which is described later in this post) while the other communication protocols available on AVR use lesser number of pins like 2 or 3. Then why does one use SPI? Here are some of the advantages of SPI:

  1. Extremely easy to interface! (It took me much less time to setup and transmit data through SPI as compared to I2C and UART!)
  2. Full duplex communication
  3. Less power consumption as compared to I2C
  4. Higher hit rates (or throughput)

And a lot more!

But there are some disadvantages as well, like higher number of wires in the bus, needs more pins on the microcontroller, etc.

Master and Slave

In SPI, every device connected is either a Master or a Slave.

The Master device is the one which initiates the connection and controls it. Once the connection is initiated, then the Master and one or more Slave(s) can transmit and/or receive data. As mentioned earlier, this is a full-duplex connection, which means that Master can send data to Slave(s) and the Slave(s) can also send the data to the Master at the same time.

As I said earlier, SPI uses 4 pins for data communication. So let’s move on to the pin description.

Pin Description

The SPI typically uses 4 pins for communication, wiz. MISO, MOSI, SCK, and SS. These pins are directly related to the SPI bus interface.

  1. MISO – MISO stands for Master In Slave Out. MISO is the input pin for Master AVR, and output pin for Slave AVR device. Data transfer from Slave to Master takes place through this channel.
  2. MOSI – MOSI stands for Master Out Slave In. This pin is the output pin for Master and input pin for Slave. Data transfer from Master to Slave takes place through this channel.
  3. SCK – This is the SPI clock line (since SPI is a synchronous communication).
  4. SS – This stands for Slave Select. This pin would be discussed in detail later in the post.

Now we move on to the SPI of AVR!

The SPI of the AVR

The SPI of AVRs is one of the most simplest peripherals to program. As the AVR has an 8-bit architecture, so the SPI of AVR is also 8-bit. In fact, usually the SPI bus is of 8-bit width. It is available on PORTB on all of the ICs, whether 28 pin or 40 pin.

Some of the images used in this tutorial are taken from the AVR datasheets.

SPI pins on 28 pin ATmega8

SPI pins on 28 pin ATmega8

SPI pins on 40 pin ATmega16/32

SPI pins on 40 pin ATmega16/32

Register Descriptions

The AVR contains the following three registers that deal with SPI:

  1. SPCR – SPI Control Register – This register is basically the master register i.e. it contains the bits to initialize SPI and control it.
  2. SPSR – SPI Status Register – This is the status register. This register is used to read the status of the bus lines.
  3. SPDR – SPI Data Register – The SPI Data Register is the read/write register where the actual data transfer takes place.

The SPI Control Register (SPCR)

As is obvious from the name, this register controls the SPI. We will find the bits that enable SPI, set up clock speed, configure master/slave, etc. Following are the bits in the SPCR Register.

SPCR Register

SPCR Register

Bit 7: SPIE – SPI Interrupt Enable
The SPI Interrupt Enable bit is used to enable interrupts in the SPI. Note that global interrupts must be enabled to use the interrupt functions. Set this bit to ‘1’ to enable interrupts.

Bit 6: SPE – SPI Enable
The SPI Enable bit is used to enable SPI as a whole. When this bit is set to 1, the SPI is enabled or else it is disabled. When SPI is enabled, the normal I/O functions of the pins are overridden.

Bit 5: DORD – Data Order
DORD stands for Data ORDer. Set this bit to 1 if you want to transmit LSB first, else set it to 0, in which case it sends out MSB first.

Bit 4: MSTR – Master/Slave Select
This bit is used to configure the device as Master or as Slave. When this bit is set to 1, the SPI is in Master mode (i.e. clock will be generated by the particular device), else when it is set to 0, the device is in SPI Slave mode.

Bit 3: CPOL – Clock Polarity
This bit selects the clock polarity when the bus is idle. Set this bit to 1 to ensure that SCK is HIGH when the bus is idle, otherwise set it to 0 so that SCK is LOW in case of idle bus.

This means that when CPOL = 0, then the leading edge of SCK is the rising edge of the clock. When CPOL = 1, then the leading edge of SCK will actually be the falling edge of the clock. Confused? Well, we will get back to it a little later in this post again.

CPOL Functionality

CPOL Functionality

Bit 2: CPHA – Clock Phase
This bit determines when the data needs to be sampled. Set this bit to 1 to sample data at the leading (first) edge of SCK, otherwise set it to 0 to sample data at the trailing (second) edge of SCK.

CPHA Functionality

CPHA Functionality

Bit 1,0: SPR1, SPR0 – SPI Clock Rate Select
These bits, along with the SPI2X bit in the SPSR register (discussed next), are used to choose the oscillator frequency divider, wherein the fOSC stands for internal clock, or the frequency of the crystal in case of an external oscillator.

The table below gives a detailed description.

Frequency Divider

Frequency Divider

The SPI Status Register (SPSR)

The SPI Status Register is the register from where we can get the status of the SPI bus and interrupt flag is also set in this register. Following are the bits in the SPSR register.

SPSR Register

SPSR Register

Bit 7: SPIF – SPI Interrupt Flag
The SPI Interrupt Flag is set whenever a serial transfer is complete. An interrupt is also generated if SPIE bit (bit 7 in SPCR) is enabled and global interrupts are enabled. This flag is cleared when the corresponding ISR is executed.

Bit 6: WCOL – Write Collision Flag
The Write COLlision flag is set when data is written on the SPI Data Register (SPDR, discussed next) when there is an impending transfer or the data lines are busy.

This flag can be cleared by first reading the SPI Data Register when the WCOL is set. Usually if we give the commands of data transfer properly, this error does not occur. We will discuss about how this error can be avoided, in the later stages of the post.

Bit 5:1
These are reserved bits.

Bit 0: SPI2x – SPI Double Speed Mode
The SPI double speed mode bit reduces the frequency divider from 4x to 2x, hence doubling the speed. Usually this bit is not needed, unless we need very specific transfer speeds, or very high transfer speeds. Set this bit to 1 to enable SPI Double Speed Mode. This bit is used in conjunction with the SPR1:0 bits of SPCR Register.

The SPI Data Register (SPDR)

The SPI Data register is an 8-bit read/write register. This is the register from where we read the incoming data, and write the data to which we want to transmit.

SPDR Register

SPDR Register

The 7th bit is obviously, the Most Significant Bit (MSB), while the 0th bit is the Least Significant Bit (LSB).

Now we can relate it to bit 5 of SPCR – the DORD bit. When DORD is set to 1, then LSB, i.e. the 0th bit of the SPDR is transmitted first, and vice versa.

Data Modes

The SPI offers 4 data modes for data communication, wiz SPI Mode 0,1,2 and 3, the only difference in these modes being the clock edge at which data is sampled. This is based upon the selection of CPOL and CPHA bits.

The table below gives a detailed description and you would like to refer to this for a more detailed explanation and timing diagrams.

SPI Data Modes

SPI Data Modes

The Slave Select (SS’) Pin

As you would see in the next section, the codes of SPI are fairly simple as compared to those of UART, but the major headache lies here: the SS’ pin!

SS’ (means SS complemented) works in active low configuration. Which means to select a particular slave, a LOW signal must be passed to it.

When set as input, the SS’ pin should be given as HIGH (Vcc) on as Master device, and a LOW (Grounded) on a Slave device.

When as an output pin on the Master microcontroller, the SS’ pin can be used as a GPIO pin.

The SS pin is actually what makes the SPI very interesting! But before we proceed, one question is that why do we need to set these pins to some value?

The answer is, that when we are communicating between multiple devices working on SPI through the same bus, the SS’ pin is used to select the slave to which we want to communicate with.

Let us consider the following two cases to understand this better:

  1. When there are multiple slaves and a single master.
    In this case, the SS’ pins of all the slaves are connected to the master microcontroller. Since we want only a specific slave to receive the data, the master microcontroller would give a low signal to the SS’ pin of that specific microcontroller, and hence only that slave microcontroller would receive data.
  2. When there are multiple masters and a single slave.
    A similar setup as above can be used in this case as well, the difference being that the SS’ lines of all the masters is controlled by the slave, while the slave SS’ line is always held low. The slave would select the master through which it has to receive data by pulling its SS’ high.Alternatively, a multiplexed system can be used where each master microcontroller can control every other master microcontroller’s SS’ pin, and hence when it has to transmit data, it would pull down every other master microcontroller’s SS’ Pin, while declaring its own SS’ as output.

You can also refer to this if you are still confused regarding Slave Select.

SPI Coded!

Up till now, we only discussed about the advantages, uses, and register description, hardware connections etc. of the SPI. Now lets see how we Code it!

Enabling SPI on Master

// Initialize SPI Master Device (with SPI interrupt)
void spi_init_master (void)
{
    // Set MOSI, SCK as Output
    DDRB=(1<<5)|(1<<3);

    // Enable SPI, Set as Master
    // Prescaler: Fosc/16, Enable Interrupts
    //The MOSI, SCK pins are as per ATMega8
    SPCR=(1<<SPE)|(1<<MSTR)|(1<<SPR0)|(1<<SPIE);

    // Enable Global Interrupts
    sei();
}

In the SPI Control Register (SPCR), the SPE bit is set to 1 to enable SPI of AVR. To set the microcontroller as Master, the MSTR bit in the SPCR is also set to 1. To enable the SPI transfer/receive complete interrupt, the SPIE is set to 1.

In case you don’t wish to use the SPI interrupt, do not set the SPIE bit to 1, and do not enable the global interrupts. This will make it look somewhat like this-

// Initialize SPI Master Device (without interrupt)
void spi_init_master (void)
{
    // Set MOSI, SCK as Output
    DDRB = (1<<5)|(1<<3);

    // Enable SPI, Set as Master
    //Prescaler: Fosc/16, Enable Interrupts
    SPCR = (1<<SPE)|(1<<MSTR)|(1<<SPR0);
}

When a microcontroller is set as Master, the Clock prescaler is also to be set using the SPRx bits.

Enabling SPI on Slave

// Initialize SPI Slave Device
void spi_init_slave (void)
{
    DDRB = (1<<6);     //MISO as OUTPUT
    SPCR = (1<<SPE);   //Enable SPI
}

For setting an microcontroller as a slave, one just needs to set the SPE Bit in the SPCR to 1, and direct the MISO pin (PB4 in case of ATmega16A) as OUTPUT.

Sending and Receiving Data

//Function to send and receive data for both master and slave
unsigned char spi_tranceiver (unsigned char data)
{
    // Load data into the buffer
    SPDR = data;

    //Wait until transmission complete
    while(!(SPSR & (1<<SPIF) ));

    // Return received data
    return(SPDR);
}

The codes for sending and receiving data are same for both the slave as well as the master. To send data, load the data into the SPI Data Register (SPDR), and then, wait until the SPIF flag is set. When the SPIF flag is set, the data to be transmitted is already transmitted and is replaced by the received data. So, simply return the value of the SPI Data Register (SPDR) to receive data. We use the return type as unsigned char because it occupies 8 bits and its value is in the range 0-255.

Problem Statement

Enough of reading, time to get your hands dirty now! Get your hardware toolkit ready and open up your software. Let’s demonstrate the working of SPI practically.

Let’s assume a problem statement. Say the given problem statement is to send some data from Master to Slave. The Slave in return sends an acknowledgement (ACK) data back to the Master. The Master should check for this ACK in order to confirm that the data transmission has completed. This is a typical example of full duplex communication. While the Master sends the data to the Slave, it receives the ACK from the Slave simultaneously.

Methodology

We would use the primary microcontroller (ATmega8 in this case) as the Master device, and a secondary microcontroller (ATmega16 in this case) as the Slave device. A counter increments in the Master device, which is being sent to the Slave device. The Master then checks whether the received data is the same as ACK or not (ACK is set as 0x7E in this case). If the received data is the same as ACK, it implies that data has been successfully sent and received by the Master device. Thus, the Master blinks an LED connected to it as many number of times as the value of the counter which was sent to the Slave. If the Master does not receive the ACK correctly, it blinks the LED for a very long time, thus notifying of a possible error.

On the other hand, Slave waits for data to be received from the Master. As soon as data transmission begins (from Master to Slave, the Slave sends ACK (which is 0x7E in this case) to the Master. The Slave then displays the received data in an LCD.

Hardware Connections

Hardware connections are simple. Both the MOSI pins are connected together, MISO pins are connected together and the SCK pins are also connected together. The SS’ pin of the slave is grounded whereas that of master is left unconnected. And then we have connected the LCD to the slave as well. Check Mayank’s tutorial on LCD interfacing. We also connect an LED to the master to demonstrate the SPI interrupt. Here are the schematics–

SPI Hardware Connections (Click to Enlarge)

SPI Hardware Connections (Click to Enlarge)

And here is how my final set up looks like–

Final Setup

Final Setup

Full Code

The codes for the Master and Slave are given below. The codes are well commented, so it should be easy to understand what is going on in the code. In case something doesn’t make sense, feel free to drop in a comment below. The full comments can viewed by scrolling the code sideways. You can also find the code in the AVR code gallery.

Master Code

#ifndef F_CPU
#define F_CPU 16000000UL
#endif

#include <avr/io.h>
#include <util/delay.h>
#include <avr/interrupt.h>

#define ACK 0x7E
#define LONG_TIME 10000

//Initialize SPI Master Device
void spi_init_master (void)
{
    DDRB = (1<<5)|(1<<3);              //Set MOSI, SCK as Output
    SPCR = (1<<SPE)|(1<<MSTR)|(1<<SPR0); //Enable SPI, Set as Master
                                       //Prescaler: Fosc/16, Enable Interrupts
}

//Function to send and receive data
unsigned char spi_tranceiver (unsigned char data)
{
    SPDR = data;                       //Load data into the buffer
    while(!(SPSR & (1<<SPIF) ));       //Wait until transmission complete
    return(SPDR);                      //Return received data
}

//Function to blink LED
void led_blink (uint16_t i)
{
    //Blink LED "i" number of times
    for (; i>0; --i)
    {
        PORTD|=(1<<0);
        _delay_ms(100);
        PORTD=(0<<0);
        _delay_ms(100);
    }
}

//Main
int main(void)
{
    spi_init_master();                  //Initialize SPI Master
    DDRD |= 0x01;                       //PD0 as Output

    unsigned char data;                 //Received data stored here
    uint8_t x = 0;                      //Counter value which is sent

    while(1)
    {
        data = 0x00;                    //Reset ACK in "data"
        data = spi_tranceiver(++x);     //Send "x", receive ACK in "data"
        if(data == ACK) {               //Check condition
            //If received data is the same as ACK, blink LED "x" number of times
            led_blink(x);
        }
        else {
            //If received data is not ACK, then blink LED for a long time so as to determine error
            led_blink(LONG_TIME);
        }
        _delay_ms(500);                 //Wait
    }
}

Slave Code

#ifndef F_CPU
#define F_CPU 16000000UL
#endif

#include <avr/io.h>
#include <avr/interrupt.h>
#include <util/delay.h>
#include "lcd.h"

#define ACK 0x7E

void spi_init_slave (void)
{
    DDRB=(1<<6);                                  //MISO as OUTPUT
    SPCR=(1<<SPE);                                //Enable SPI
}

//Function to send and receive data
unsigned char spi_tranceiver (unsigned char data)
{
    SPDR = data;                                  //Load data into buffer
    while(!(SPSR & (1<<SPIF) ));                  //Wait until transmission complete
    return(SPDR);                                 //Return received data
}

int main(void)
{
    lcd_init(LCD_DISP_ON_CURSOR_BLINK);           //Initialize LCD
    spi_init_slave();                             //Initialize slave SPI
    unsigned char data, buffer[10];
    DDRA  = 0x00;                                 //Initialize PORTA as INPUT
    PORTA = 0xFF;                                 //Enable Pull-Up Resistors
    while(1)
    {
        lcd_clrscr();                             //LCD Clear screen
        lcd_home();                               //LCD move cursor to home
        lcd_puts("Testing");
        lcd_gotoxy(0,1);
        data = spi_tranceiver(ACK);               //Receive data, send ACK
        itoa(data, buffer, 10);                   //Convert integer into string
        lcd_puts(buffer);                         //Display received data
        _delay_ms(20);                            //Wait
    }
}

Video

Here is short demonstration of how this setup operates.

Using Interrupts

In case you are interested in using the SPI Interrupt of the AVR, you should keep in mind the following things–

  • Be sure to include #include <avr/interrupt.h> header.
  • Set the SPIE bit to 1 in the SPCR register.
  • Enable global interrupts using sei().

Next thing is to write an Interrupt Service Routine (ISR), which can be written something like this–

// SPI Transmission/reception complete ISR
ISR(SPI_STC_vect)
{
    // Code to execute
    // whenever transmission/reception
    // is complete.
}

If you want to know what are interrupts, may be this little introduction might be useful.

Summary

Let’s have a look what we have learnt in this post–

  • SPI is a serial communication protocol where all the devices are classified as either Master or Slave. It requires four pins to communicate – MISO, MOSI, SCK and SS’.
  • Almost every AVR microcontroller supports SPI and has some specific pins multiplexed with SPI functionality.
  • In AVR microcontrollers, the three registers which monitors the SPI are SPCR, SPSR and SPDR.
  • SPI has four data modes of operation.
  • Multiple slaves can be connected to the same SPI bus using Slave Select (SS’) pin.
  • We have also discussed how to program the SPI of an AVR with a relevant example featuring full duplex communication between two AVR microcontrollers.

So that’s it folks! In serial communication, we have discussed about RS232, UART/USART and SPI. Next is I2C! Subscribe to maxEmbedded so as not to miss upon any updates! Let us know your views, queries and comments through the text box at the end of this post.

Thank you.

Written by Yash Tambi and Mayank (Max) Prasad
support@maxEmbedded.com

Last updated on February 9, 2016

81 Comments

  1. Thanks for that Motorola doc. Was yearning for something like that! Nice tut,as usual :-)

  2. You’re passing the number 10000 as uint8_t to the led_blink().

    • Thanks for pointing it out. Now corrected! :)

  3. Very well worked out tutorial. Thanks a lot. But I have a doubt, when i implement the above code and circuit, the ack I send keeps getting ignored since there is a SPDR write collision. I have no idea why this happening.

    And will the interrupt be called after the SPDR is filled with the received data just like it is called when transmission is complete???

    • It could happen if you haven’t programmed your Master and Slave properly. Remember that you need to program them so that they operate in sync.

      Yes, interrupt occurs when an entire SPI transaction is complete, which means both, transmission and reception.

  4. Hi

    There seems one mistake in Enable SPI as Master

    // Set MOSI, SCK as Output
    DDRB=(1<<5)|(1<<3);
    ^^^^^^^
    SCK is the 7th bit of PORTB
    This should left-shift by 7??

    • Sorry, I think the AVR I use is not the same as your tutorial~

    • It is for ATmega8. What you’re saying is for ATmega32.

  5. I think there is a mistake in the connections and MOSI MISO scheme shown

  6. Thanks……..

    • This doesn’t work in the master mode. You missed the SS definitions.
      In the master mode the SS should defined as output or as input and holds Hi.
      This should be in the Init subroutine.
      DDRB = (1<<5)|(1<<3)|(1<<2);

  7. The circuit diagram here shows the MISO pin of one uC connected to the MOSI pin of the other and vice versa. This connection is incorrect. The MISO of one should be connected to the MISO of the other and the same goes for MOSI. Please have a look.

    • Thanks Shashwat. Corrected it. It has been long overdue and I have been procrastinating it!

  8. Hi Mayank. This might be not the best thread to post my question. I appologize for that.
    I’m using SPI to communicate between ATmega328P and Adesto AT45DB041E memory chip. I’m printing some images on the screen using oled technology. The problem is sometime during plugging and unplugging the USB some of the images gets corrupted(usually the first one). What could the possible reason for this corruption. Please let me know if you need any other info regarding this.

  9. What is the purpose of these two lines in the slave device code?
    I can’t correspond them to the schematic.

    DDRA = 0x00; //Initialize PORTA as INPUT
    PORTA = 0xFF; //Enable Pull-Up Resistors

    • You can skip them. They are not used here.

  10. Hi Max, thank you for the exellent tutorial. I met a problem that I want to use a master AVR to communicate with 100 slave AVRs with high speed 10M bps. Since I do not have that much pins to active/deactive 100 slave SS pins, is it possible that let all slave SS be connected to ground (all slaves stay active during SPI), so that the master AVR can send data to all the slave AVRs at one time ? And how about your opinion with such 1 master 100 slaves AVR communication ? Thank you! :)

    • That may not work since SPI is full duplex. You’ll need to use some sort of multiplexer for the SS pins.

  11. Hello guys!!! this program is not working for me!!! i am using 2 atmega 32 micro controllers ,,, and spdr register values are not interchanging,, they are keeping their own values only,, so no transmission i think,, can you please help me!! i need one way communication only!!!!

    • Did you change the Master code to work with ATmega32?

  12. hello , sir
    My self kishor patel and my question is how to interface code for ADS1278 SIGMA DELTA ADC with PSOC CONTROLLER dvk3(DEVLOPMENT KIT 3) kit using SPI PROTOCOL ……. PLZ SIR give me fast response as soon as possible ..beacuse its too ugently and i am so confused,,so plz sir give me solution

  13. can we send and receive multi byte data.. ? can you explain it pls.

    • Yes, byte by byte. You need to send it one byte at a time.

  14. in the lat code that you suggested how about we use 8-bit frame and the most significant would for parity checking that way we implement error checking plus the aknolegment

    • SPI doesn’t need acknowledgement. I2C does.

      • I know its just that acknowledgment is such a great thing about I2C it keep the transmitter updated with the statues of the receiver if its receiving or the data is transmitted no where like you said the master in spi could be sending data to nothing and doesn’t know about it

  15. Hello sir ,i dint understood abt acknowledgement u mean that master ill hv to receive entire data what it has sent to slave to check wheter it is sent properly r not.

    • Acknowledgement (in the above example) is a data that the slave sends back to master specifying successful data transmission. This acknowledgement is different from the ACK bit in I2C protocol.

  16. How sending data back from slave to master act as acknowledgement ??

Trackbacks/Pingbacks

  1. SPI Communication between Atmega32 and Atmega8 - […] you read this ? http://maxembedded.com/2013/11/the-spi-of-the-avr/…
  2. Noise reduction in SPI | DL-UAT - […] just to check my SPI code, I tried to establish communication b/w two ATmega16A µC as per this tutorial.…
  3. Serial Peripheral Interface (SPI) pada AVR - […] Referensi :  maxembedded.com […]

Leave a Reply

%d bloggers like this: