Wednesday, 14 November 2012

SPI : Connecting Three Slaves To a Master Microcontroller Using ATmega16

Introduction to SPI:

SPI stands for Serial Peripheral Interface, it allows high-speed synchronous data transfer between the ATmega16 and peripheral devices (or between several AVR devices). It has the following properties:
  • Full-duplex
  • Three-wire Synchronous Data Transfer
  • Master and Slave Operation

How It Works:

  • The Master initiates the communication cycle when pulling low the Slave Select SS pin.
  • Writing a byte to the Master Data Register (Master SPDR) starts the clock on the SCK line.
  • After shifting one byte, the clock stops, and the flag SPIF is set.
  • If the SPI interrupt enable bit (SPIE) is set, an interrupt is requested, and the Master will synchronize the Slave by pulling high the SS line.

Connection:

  1. MOSI (PB5 in ATmega16): Master Output Slave Input. For master micro-controller this pin is output and for slave micro-controller this pin is input.
  2. MISO (PB6 in ATmega16): Master Input Slave Output. For master micro-controller this pin is input and for slave micro-controller this pin is output.
  3. SCK (PB7 in ATmega16): SPI clock. For master micro-controller this pin is output and for slave micro-controller this pin is input.
  4. SS (active low) (PB4 in ATmega16): Slave Select or Slave enable. For master micro-controller this pin is output and for slave micro-controller this pin is input.
The four pins of SPI communication is connected as the above figure.

Connecting More Than One Slave:

You can connect more than one slave SPI module to one Master module. You will have to add a slave select line in the master module for every slave you add as in the following picture:

Testing:

In this test I have connected three SPI modules of ATmega16 as slaves to one master SPI module of ATmega16. I used PB1 as slave enable for slave number 1, PB3 as slave enable for slave number 2 and PB4 as slave enable fo slave number 3. I used 8 DIP switches connected to PORTA for the master and the three slaves to change the data to be sent between them. I used the three external interrupts of ATmega16 to select which slave to send the data to (i.e. to send data from master to slave number one press the button connected to INT0 and so on). The data sent is displayed on PORTC.



The SPI specifications used in the testing:

// SPI initialization
// SPI Clock Rate: 250.000 kHz
// SPI Clock Phase: Cycle Half
// SPI Clock Polarity: Low
// SPI Data Order: MSB First

Sample of the master code:

// External Interrupt 0 service routine
interrupt [EXT_INT0] void ext_int0_isr(void)
{
// Place your code here
PORTB.1=0;     // enable slave no.1
SPDR=PINA;   // read the data from PORTA and send it
}

// External Interrupt 1 service routine
interrupt [EXT_INT1] void ext_int1_isr(void)
{
// Place your code here
PORTB.3=0;   // enable slave no.2
SPDR=PINA;   // read the data from PORTA and send it
}

// External Interrupt 2 service routine
interrupt [EXT_INT2] void ext_int2_isr(void)
{
// Place your code here
PORTB.4=0;   // enable slave no.3
SPDR=PINA;   // read the data from PORTA and send it
}

// SPI interrupt service routine
interrupt [SPI_STC] void spi_isr(void)
{
unsigned char data;
data=SPDR;
// Place your code here
PORTC=data;     // dispaly data on PORTC
PORTB.4=1;PORTB.3=1;PORTB.1=1;  // disable all the slaves
}

// Declare your global variables here

void main(void)
{
PORTA=0x00;
DDRA=0x00;

PORTB=0x1A;
DDRB=0xBA;

PORTC=0x00;
DDRC=0xFF;

// External Interrupt(s) initialization
// INT0: On
// INT0 Mode: Rising Edge
// INT1: On
// INT1 Mode: Rising Edge
// INT2: On
// INT2 Mode: Rising Edge
GICR|=0xE0;
MCUCR=0x0F;
MCUCSR=0x40;
GIFR=0xE0;

// SPI initialization
SPCR=0xD0;
SPSR=0x00;

// Clear the SPI interrupt flag
#asm
    in   r30,spsr
    in   r30,spdr
#endasm

// Global enable interrupts
#asm("sei")

while (1)
      {
      // Place your code here
      };
}

Sample of the Slave code:

// SPI interrupt service routine
interrupt [SPI_STC] void spi_isr(void)
{
unsigned char data;
data=SPDR;
// Place your code here
PORTC=data ;
}

void main(void)
{
PORTA=0x00;
DDRA=0x00;

PORTB=0x00;
DDRB=0x40;

PORTC=0x00;
DDRC=0xFF;

// SPI initialization
SPCR=0xC0;
SPSR=0x00;

// Clear the SPI interrupt flag
#asm
    in   r30,spsr
    in   r30,spdr
#endasm

// Global enable interrupts
#asm("sei")

while (1)
      {
      // Place your code here
      SPDR=PINA;
      };
}

You can download the project and simulation files from the following link:
You can download this tutorial in PDF format from the following link: