Вы находитесь на странице: 1из 8

AUTHOR: ZOLTAN MOLNAR KEYWORDS: SPI, HIGH SPEED, MASTER, SLAVE, COMMUNICATION

DESIGN NOTE
#035

This document is originally distributed by AVRfreaks.net, and may be distributed, reproduced, and modified without restrictions. Updates and additional design notes can be found at: www.AVRfreaks.net

Using the SPI as High Speed, Bi-directional Data Bus with Automatic Master/Slave Switch
Introduction
While the UART offers asynchronouse communication support, there are some situations (dual controller systems), where high speed (Mbps) communication between two microcontrollers is desirable. For this kind of applications the SPI bus seems to be a good solution because its high speed, but there are some limitations such as: The SPI interface is a synchronouse data bus, where data transfers are initiated by the SPI Bus Master Data transfers cant be initiated by the bus slave Data transfer from the Slave to the Master is possible only while the Master is writing into the Slave.

This design note shows how to achieve high speed, asynchronouse, half-duplex, interrupt driven communication with the hardware SPI, between two AVR microcontrollers.

Overview

To send data from the SPI Slave to the SPI Master, a Master/Slave switch is performed whenever one of the SPI devices want to transmit data to the other. The communication is half-duplex. This solution takes full use of the capability of the AVRs SPI port wich will switch to Slave mode if in Master mode the SS pin is driven low. The SPI interconnection between the two controllers is shown in Figure 1 bellow. The two AVRs have the SS pin of the other AVR wired to an output pin. Initially one of the controllers is in Master mode, the other in Slave mode. The Master controller has its SS pin configured as output, so Master/Slave switch is not possible. The Master controller will keep in low the SS pin of the Slave controller selecting as active SPI Slave. The Slave controller keeps the SS pin of the Master high till it is in Slave mode. After transmitting the first message from the Master to the Slave, the Master controller will configure its SS pin as input, enabling Master/Slave role switch when the Slave controller drives low the SS pin of the Master.

www.AVRfreaks.net

1 Design Note #035 Date: 06/02

Figure 1. SPI Interconnection between Two Controllers

40 41 42 43 44 1 2 3 4 5 6

AT90S8515

U1

PA3(AD3) PA2(AD2) PA1(AD1) PA0(AD0) Vcc nc PB0(T0) PB1(T1) PB2(AIN0) PB3(AIN1) PB4(SS)

39 38 37 36 35 34 33 32 31 30 29

PC4(A12) PC3(A11) PC2(A10) PC1(A9) PC0(A8) nc GND XTAL1 XTAL2 PD7(RD) PD6(WR)

28 27 26 25 24 23 22 21 20 19 18

AT90S8515

Host application controller

BLUEPORT controller

The Master/Slave switch mechanism is interrupt driven, so no additional load is put on the MCU (no waiting loops). A data transmission is initiated by the putSPI(u08 data) API that will check if the SPI port is in Master mode. If so putSPI(u08 data) will put one data byte into SPDR. If the SPI port is in Slave mode, putSPI(u08 data) will initialize a Master/Slave transition in the other controller by driving its SS pin low. Untill a controller is switching mode, the SPI interface is disabled. When the controller is in Slave mode, it will drive high the SS pin of the controller initiating the role switch. This is noticed, the SPI interface re-enabled in Master mode and data transmission started. While the controller is in Master mode and transmitting a message its SS pin is configured as output, so no accidental Master/Slave switch can happen during the transfer. Master/Slave switch is enabled only after transmitting a whole message. While in Master mode and transmitting the SPI transmission complete interrupt will fetch data from the source FIFO to the SPDR till the message is transmitted. If not transmitting and controller is in Master mode, a transmission complete interrupt will signal a Master/Slave role switch request, so the SPI interface will be disabled, reconfigured as Slave and re-enabled. If the SPI interface is in Slave mode, a transmission complete interrupt will signal the reception of a message byte, so the content of the SPDR will be stored in the SPI receive FIFO. This mechanism enables that high speed, asynchronous, half-duplex bi-directional data transmission to be performed through the synchronous SPI bus. If the SS Selection pin of the AVR is wired as an external interrupt source, the same mechanism can be used even with microcontrollers that doesnt have the AVR SPI port abilities.

www.AVRfreaks.net

Design Note #035 Date: 06/02

18 19 20 21 22 23 24 25 26 27 28

PD6(WR) PD7(RD) XTAL2 XTAL1 GND nc PC0(A8) PC1(A9) PC2(A10) PC3(A11) PC4(A12)

PA4(AD4) PA5(AD5) PA6(AD6) PA7(AD7) ICP nc ALE OC1B PC7(A15) PC6(A14) PC5(A13)

PB5(MOSI) PB6[MISO) PB7[SCK) RESET PD0(RxD) nc PD1(TxD) PD2(INT0) PD3(INT1) PD4 PD5(OC1A)

7 8 9 10 11 12 13 14 15 16 17

7 8 9 10 11 12 13 14 15 16 17

PB4(SS) PB3(AIN1) PB2(AIN0) PB1(T1) PB0(T0) nc Vcc PA0(AD0) PA1(AD1) PA2(AD2) PA3(AD3)

6 5 4 3 2 1 44 43 42 41 40

U1

PB5(MOSI) PB6[MISO) PB7[SCK) RESET PD0(RxD) nc PD1(TxD) PD2(INT0) PD3(INT1) PD4 PD5(OC1A)

PA4(AD4) PA5(AD5) PA6(AD6) PA7(AD7) ICP nc ALE OC1B PC7(A15) PC6(A14) PC5(A13)

39 38 37 36 35 34 33 32 31 30 29

Code

Here is an example application where both of the microcontrollers uses its UART to receive data wich is transmitted through the SPI to the other controller. Data received from the SPI will be transmitted through the UART, achieving UART to UART communication. This sample application can serve as backbone for half-duplex protocol translators or other similar applications. The bi-directional SPI communication library has the following API: Void SPImaster(void) set up SPI&port pins for Master mode. Void SPIslave(void) set up SPI&port pins for Slave mode. Void initSPI_RX(t_fifo* pfifo) initialize interrupt based reception into a FIFO. Void putSPI(u08 data) send one byte through the SPI and perform automatic Master/Slave switch if required (controllers SPI is in Slave mode).

The application uses two other librarys: A circular FIFO and an UART communication library. The circular FIFO library offers dynamic allocation of circular FIFOs, commonly used in communication applications. The UART communication library supports interrupt based data reception into a circular FIFO, interrupt based transmission from a circular FIFO as well polled mode byte transmission. The application code is the same in both microcontrollers, to learn more about the FIFO and the UART communication library and to download the full source code please visit http://ww.microhardsoft.igo.com

Design Note #035 Date: 06/02

www.AVRfreaks.net

/*************************************************************************** Sample application showing asynchronouse, half-duplex SPI communication. ***************************************************************************/ #include <io.h> #include <interrupt.h> #include "global.h" #include "hardware.h" #include "fifo.h" #include "uart.h" #include "spi_bidir.h" //========================================================================== t_fifo HWUARTrx_fifo; t_fifo SPIrx_fifo; //hardware UART receive FIFO //hardware SPI receive FIFO

//========================================================================== int main (void) { /*set up port pin directions and levels*/ outp(0xFF,DDRB); outp(0xFF,PORTB); outp(0xFF,DDRD); outp(0xFF,PORTD);

//init. hardware UART receive FIFO if(!initFIFO(&HWUARTrx_fifo, 100)) { for(;;){} } //try to create and initialize a 100 byte circular fifo //trap memory allocation errors

//init. hardware SPI receive FIFO if(!initFIFO(&SPIrx_fifo, 100)) { for(;;){} } //try to create and initialize a 100 byte circular fifo //trap memory allocation errors

setupUART0(57600,&HWUARTrx_fifo); initSPI_RX(&SPIrx_fifo); SPImaster();

//initialize UART speed and receive FIFO //initialize SPI receive FIFO //initialize SPI master mode

sei();

//enable interrupts

//MAIN program loop for(;;) { if(!HWUARTrx_fifo.isempty) { u08 data=getFIFO(&HWUARTrx_fifo); putSPI(data); } //if it is, extract byte/byte //send through SPI //check if there is data in HWUART input FIFO

www.AVRfreaks.net

Design Note #035 Date: 06/02

if(!SPIrx_fifo.isempty) { u08 data=getFIFO(&SPIrx_fifo); putch0(data); } }

//check if there is data in SPI input FIFO

//if it is, extract byte/byte //send through UART

return 0; }

Here is the code for the SPI driver: /*************************************************************************** Header file for bi-directional SPI communication support. ***************************************************************************/ #ifndef SPI_H #define SPI_H

/*init. SPI, master mode*/ extern void SPImaster(void);

/*init. SPI slave mode*/ extern void SPIslave(void);

/*Init. SPI receive FIFO*/ extern void initSPI_RX(t_fifo* pfifo);

/*Send a byte through the SPI, perform master/slave switch if SPI is in slave mode*/ extern void putSPI(u08 data);

#endif /*************************************************************************** Implementation of bi-directional SPI communication library. ***************************************************************************/ #include <io.h> #include <sig-avr.h> #include "global.h" #include "hardware.h" #include "fifo.h" #include "spi_bidir.h"

#define SPIport PORTB #define SPIpins PINB #define SPIdir DDRB

//SPI interface port pin levels //SPI interface port pin levels //SPI interface port pin directions

#if defined (__AVR_ATmega103__) || defined (__ATmega103__) #define SS 0 //SPI SS pin

/*on ATMEGA103 SPI is on PB.0-4*/

Design Note #035 Date: 06/02

www.AVRfreaks.net

#define SSsel 4

//SS pin for SPI peer

#define SPImask 0x1F

#define SPIdir_S 0xf8 #define SPIlvl_S 0xff

#define SPIdir_M 0xf6 #define SPIlvl_M 0xef #else #define SS 4 #define SSsel 3 /*on regular AVRs SPI is on PB.3-7*/ //SPI SS pin //SS pin for SPI peer

#define SPImask 0xf8

#define SPIdir_S 0x4f #define SPIlvl_S 0xff

#define SPIdir_M 0xaf #define SPIlvl_M 0xf7 #endif

t_fifo* pSPIrx_fifo; u08 SPImode; volatile u08 SPIstatus; /*init. SPI, master mode*/ void SPImaster(void) { u08 temp=inp(SPIdir); temp=temp | SPImask; temp=temp & SPIdir_M; outp(temp,SPIdir);

//pointer to hardware SPI receive FIFO //previouse SPI state: if 1 master/if 0 slave

//prepare pin directions for SPI master mode

temp=inp(SPIport); temp=temp | SPImask; temp=temp & SPIlvl_M; outp(temp,SPIport);

//prepare pin levels for SPI master mode

loop_until_bit_is_set(SPIpins,SS); outp(0xF8,SPCR); SPImode=1; } /*init. SPI slave mode*/ void SPIslave(void) { u08 temp=inp(SPIdir);

//wait till SPI peer is slave //set up SPI control register //signal master

//prepare pin directions for SPI slave mode

www.AVRfreaks.net

Design Note #035 Date: 06/02

temp=temp | SPImask; temp=temp & SPIdir_S; outp(temp,SPIdir);

temp=inp(SPIport); temp=temp | SPImask; temp=temp & SPIlvl_S; outp(temp,SPIport); outp(0xE8,SPCR); SPImode=0; }

//prepare pin levels for SPI slave mode

//set up SPI control register //signal slave

/*Init. SPI receive FIFO*/ void initSPI_RX(t_fifo* pfifo) { pSPIrx_fifo=pfifo; } /*Send a byte through the SPI, perform master/slave switch if SPI is in slave mode*/ void putSPI(u08 data) { if(bit_is_clear(SPCR,MSTR)) { cbi(SPCR,SPIE); cbi(SPCR,SPE); cbi(SPIport,SSsel); while(bit_is_clear(SPIpins,SS)) {}; SPImaster(); } //wait till SS is driven high by former master -> now its slave //became an SPI master //disable SPI interrupts //disable SPI interface -> PortB, pin SS becomes a gen. input pin //SS pin of master to low -> init. master -> slave transition //verify that we are in slave mode

outp(data,SPDR); SPIstatus=1; while(SPIstatus){}; }

//send first data byte //we are transmitting through SPI //wait till byte is transferred

/*interrupt handler for SPI transmission complete interrupt. Handles transmit/receive/master-to-slave transition. */ SIGNAL(SIG_SPI) { if(bit_is_set(SPCR,MSTR)) { SPIstatus=0; } else { if(!SPImode) { //receive interrupt //receive complete or master-to-slave interrupt //byte is sent through SPI //transmit complete interrupt condition

Design Note #035 Date: 06/02

www.AVRfreaks.net

u08 SPItemp=inp(SPDR); if(!(pSPIrx_fifo->isfull)) { cbi(PORTD,LED); putFIFO(pSPIrx_fifo,SPItemp); } } else { cbi(SPCR,SPIE); cbi(SPCR,SPE); SPIslave(); } } }

//load received character

//master to slave transition //disable SPI interrupts //disable SPI interface //became an SPI slave

www.AVRfreaks.net

Design Note #035 Date: 06/02

Вам также может понравиться