Serial peripheral interface in AVR microcontrollers

Serial Peripheral Interface (SPI) is fastest synchronous communication interface allowing data transfer speeds up to half of core clock. If AVR microcontroller is clocked at 16MHz then SPI clock may reach 8MHz in master mode. SPI communication interface is common way to talk to other peripherals around MCU like flash, EEPROM, sensors and even other microcontrollers.

Generally speaking devices communicate over SPI interface using four wires MISO (Master In Slave out), MOSI (Master Out Slave In), SCK (synchronization clock) and SS (Slave Select). Usually if only one slave device is used SS line is omitted while slave chip select pin is tied to ground. How ever this is a special case in all other cases SS pin has to be controlled manually in software – this isn’t handled automatically. If more slaves are connected to SPI interface there are options in selecting right slave device: one is to use dedicated SS pins for each slave or if slave supports this use address bytes in data packets to select one (for instance in MCP23S17 I/O expanders).

SPI master two slaves

SPI peripheral in AVR can operate in Master or Slave modes. If master mode is selected then it takes care of generating clock signal and starting transfer. Slave only waits for SS line to pull down to get ready for transfer.

In any mode master and slave shift registers are connected in a ring, what means full duplex communication. Simply speaking if master shifts out one byte to slave it receives byte from slave as long as both – MISO and MOSI lines are connected. So if master wants to read single byte from slave it has to shift dummy byte to receive one.

AVR in master and slave SPI mode

To demonstrate AVR working in master and slave SPI modes we are going to connect two Atmega328P microcontrollers. One will be master and second slave SPI device. First of all lets take a look at master SPI. In this mode we are going to program AVR continuously send incrementing 8-bit value via SPI device every 10ms. Program doing this is really simple:

//SPI master
#include <avr/io.h>
#include <util/delay.h>
//SPI init
void SPIMasterInit(void)
//set MOSI, SCK and SS as output
DDRB |= (1<<PB3)|(1<<PB5)|(1<<PB2);
//set SS to high
PORTB |= (1<<PB2);
//enable master SPI at clock rate Fck/16
SPCR = (1<<SPE)|(1<<MSTR)|(1<<SPR0);
//master send function
void SPIMasterSend(uint8_t data)
//select slave
PORTB &= ~(1<<PB2);
//send data
//wait for transmition complete
while (!(SPSR &(1<<SPIF)));
//SS to high
PORTB |= (1<<PB2);
int main(void)
//initialize master SPI
//initial PWM value
uint8_t pwmval = 0;
while (1)

First of all we have to configure SPI device where MOSI, SCK and SS pins has to be configured as output. SS pin has to be kept high when data isn’t shifted out through SPI. And also we need to enable SPI device, set it to master and select clock rate (by setting SPE, MSTR and SPR0 bits in SPCR register). In our case we simply chose it to be Fck/16. As our Atmega328p is clocked at 16MHz then SPI transfer rate becomes 1mb/s, or simply speaking data is shifted out at 1MHz clock rate. Once SPI is initialized we have to implement data transfer function. Before sending any data first we have to enable SPI slave by pulling SS pin low. Then we simply write byte to SPDR register and wait until transfer is complete. In a main loop we simply send incrementing byte value every 10ms.

Once value overflow it simply starts over from zero. This is all we need from master.

Then lets get to slave device. For this we are using another Atmega328P board clocked at 16MHz. To indicate that SPI transfer is successful we are generating PWM signal on OC0A pin where LED is attached. PWM is generated with Timer0 in fast PWM mode. There PWM duty cycle is controlled with OCR0A register value. In slave this value will be updated from SPI. As we made master send incrementing byte value, so our PWM signal will be varying from 0% to 100% duty cycle leading to fading LED effect. Here is a Slave SPI code:

//SPI slave
#include <avr/io.h>
#include <avr/interrupt.h>
//SPI init
void SPISlaveInit(void)
//set MISO as output
DDRB |= (1<<PB4);
//enable SPI and enable SPI interrupt
SPCR = (1<<SPE)|(1<<SPIE);
void InitPort(void)
//set PD6 (OC0A) as output
//Initialize Timer0
void InitTimer0(void)
//Set Initial Timer value
//Place compare value to Output compare register
//Set fast PWM mode
//and make clear OC0A on compare match
void StartTimer0(void)
//Set prescaller 64 and start timer
int main(void)
//initialize slave SPI
while (1)

In slave we have set up a SPI transfer complete interrupt service routine which will be called each time a SPI transfer is complete. ISR simply updates OCR0A register with received value.

Actually this is it. SPI is really simple to control. And with this simplicity you get high speed synchronous data transfer.

It is worth to mention that AVR microcontroller USART module can be set to work in SPI master mode. It is somehow a little bit limited version since it is build using USART resources but still you get fully functioning master SPI.

Bookmark the permalink.


  1. The picture with master and two slaves is wrong. MISO arrow at the ‘slave 2’ block is wrong.

  2. Thank you for notice. It will be fixed soon.

  3. Excellent Tutorial, thank You

Leave a Reply

Your email address will not be published. Required fields are marked *