Difference between revisions of "SPI communication between PICs"
SamPickerill (talk | contribs) |
|||
(35 intermediate revisions by 5 users not shown) | |||
Line 1: | Line 1: | ||
__TOC__ |
|||
== Original Assignment == |
|||
== Overview == |
|||
SPI or [http://en.wikipedia.org/wiki/Serial_Peripheral_Interface_Bus Serial Peripheral Interface] is a communication method that was once used to connect devices such as printers, cameras, scanners, etc. to a desktop computer. This function has largely been taken over by USB, but SPI can still be a useful communication tool for some applications. SPI runs using a master/slave set-up and can run in full duplex mode, meaning that signals can be transmitted between the master and the slave simultaneously. There is no standard communication protocol for SPI. |
|||
SPI is still used to control some peripheral devices and has some advantages over [[I2C communication between PICs|I2C]] (another type of serial data communication). SPI can communicate at much higher data rates than I2C. Furthermore, when multiple slaves are present, SPI requires no addressing to differentiate between these slaves. Compared to parallel buses, SPI has the additional benefit of requiring only simple wiring. |
|||
Microcontrollers commonly talk to each other using protocols such as RS-232, SPI (for "serial peripheral interface"), I2C (or I^2C, "I squared C," for inter-integrated circuit), CAN bus, or other. This project is to demonstrate bidirectional SPI communication between two PICs. A demonstration might have the value of an analog input (potentiometer) at one PIC displayed as a "light bar" on the LEDs of the other PIC board, and simultaneously vice-versa. |
|||
== Overview == |
|||
SPI or [http://en.wikipedia.org/wiki/Serial_Peripheral_Interface_Bus Serial Peripheral Interface] is a communication method that was once used to connect devices such as printers, cameras, scanners, etc. to a desktop computer. This function has largely been taken over by USB, however SPI can still be a useful communication tool for some applications. SPI runs as a master slave set-up and can run in full duplex mode, meaning that signals from the master to the slave and vis versa can be transferred at the same time. |
|||
SPI involves four lines, and is therefore often termed the “four wire” serial bus. The four lines are: |
|||
Peripheral devices that still use SPI: |
|||
SCLK — Serial Clock (output from master) |
|||
• Converters (ADC and DAC) |
|||
MOSI/SIMO — Master Output, Slave Input (output from master) |
|||
• Memories (EEPROM and FLASH) |
|||
MISO/SOMI — Master Input, Slave Output (output from slave) |
|||
• Real Time Clocks (RTC) |
|||
SS — Slave Select(active low; output from master) |
|||
• Sensors (temperature, pressure, etc.) |
|||
• Others (signal mixer, potentiometer, LCD controller, UART, CAN controller, USB controller, amplifier) |
|||
The Master controls all communication. It starts the clock, decides when data is sent and received and selects the slaves to communicate with (SS line). Within each clock cycle a full duplex communication is carried out. Each bit is shuffled into the slave line and a bit is shuffled from the slave line back to the master line. The SS line selects the slave to transfer data with. Multiple slaves may be selected with an output low. If a slave is not selected they must disregard signals sent by the master. Although possible it is rare for a multi-master system. |
|||
The master first sends logic low to select the slave. There is no general protocol for the transmission. The slave can then either just receive or receive and reply to the Master. The Master can also either just send data or send and receive data. |
|||
SPI is still used to control some peripheral devices and has some advantages over I2C (another type of serial data communication). SPI can send much higher rates of data than I2C and requires no addressing to separate slave devices. Simple wiring as compared to parallel buses are also a benefit. |
|||
Below are some devices that still use SPI. |
|||
<b>Basic Operation</b> |
|||
Peripheral Devices |
|||
SPI requires four lines, and is therefore often termed the “four wire” serial bus. These four lines are described in the table below. |
|||
• Converters (ADC and DAC) |
|||
• Memories (EEPROM and FLASH) |
|||
{| border="1" cellspacing="2" cellpadding="3" align="center" |
|||
• Real Time Clocks (RTC) |
|||
|- |
|||
• Sensors (temperature, pressure) |
|||
!Line!! Name!! Description |
|||
• Others (signalmixer, potentiometer, LCD controller, UART, CAN controller, USB controller, amplifier) |
|||
|- |
|||
|SCLK||Serial Clock||Output from master |
|||
|- |
|||
|MOSI/SIMO||Master Output, Slave Input||Output from master |
|||
|- |
|||
|MISO/SOMI||Master Input, Slave Output||Output from slave |
|||
|- |
|||
|SS||Slave Select||Output from master (active low) |
|||
|- |
|||
|} |
|||
The master, as its name suggests, controls all communication. By controlling the clock, the master decides when data is sent and received. Within each clock cycle a full duplex communication is carried out; each side sends and receives one bit of information. Because there is no standard communication protocol, the master can either send data or both send and receive data, depending on the needs of the application. Likewise, the slave can either receive data or both receive and send data back to the master. |
|||
Using the “Slave Select” line, the master chooses which slave with which to communicate. Note that more than one slave may be selected, simply by applying a logic low to the desired SS lines, as illustrated in the schematic diagram shown above. If a given slave is not selected (its SS is high) it disregards signals sent by the master. |
|||
<b>References</b> |
|||
[http://www.totalphase.com/support/articles/article03/ SPI Background](www.totalphase.com) |
|||
[http://en.wikipedia.org/wiki/Serial_Peripheral_Interface_Bus SPI Wikipedia Article] (www.wikipedia.org) |
|||
[http://www.mct.net/faq/spi.html More Information] (www.mct.net) |
|||
References |
|||
[http://www.totalphase.com/support/articles/article03/#modes SPI Background].www.totalphase.com |
|||
[http://en.wikipedia.org/wiki/Serial_Peripheral_Interface_Bus Serial Peripheral Interface]. Www.wikipedia.org |
|||
[http://www.mct.net/faq/spi.html mct.net] |
|||
== Circuit == |
== Circuit == |
||
<b>Master connected to three slaves:</b> |
|||
[[Image:spi-diagram.png]] |
[[Image:spi-diagram.png]] |
||
Here each slave must be enabled through the slave select pin in order to communicate with the Master. |
|||
<b>One PIC master and one PIC slave:</b> |
|||
[[Image:spi.jpg]] |
|||
The master, on the left, and the slave, on the right, are connected as shown above. Be sure you also connect grounds! |
|||
Above shows the Master connected to three slaves. Each slave must be enabled through the slave select pin in order to communicate with the Master. The two PICs can be wired directly by connecting these input and output pins of the diagram above. |
|||
[[Image:PIC18f4520 spi diagram 2.jpg]] |
|||
== Code == |
== Code == |
||
SPI communication on the PIC is accomplished via the functions spi_read() and spi_write(), which read and write one byte at a time over SPI. In the example below, the master reads an 8-bit value from the analog-to-digital converter and sends it to the slave via SPI. The slave reads the value and displays it on an LCD display. |
|||
<b>Note:</b> The code given below does not work perfectly: the slave failed to received 10-20% of the messages sent by the master. In this example that failure rate was not critical, as the slave just missed an update of the ADC reading when it missed a message. If more accurate data transmission is required, the failure rate inherent in this system may be unacceptable. |
|||
<pre> |
|||
/*MASTER CODE*/ |
|||
/* This code is valid only for devices that support hardware spi such as the PIC18F4520. For all other devices use |
|||
software spi. This code does not work on PIC18F4520 and will require more experimentation. This is the code that |
|||
looks most promising to start with from our review of codes on the internet for hardware SPI*/ |
|||
/* For hardware spi, library functions such as spi_setup(), spi_read() and spi_write() can be used */ |
|||
/* For software spi, spi_xfer() can be used alongwith the #use spi directive*/ |
|||
/* We are not entirely sure if an output pin of the master should be connected to the SS pin (A5 for PIC18f4520) |
|||
of the slave and so we have commented out code that should be included if an output pin (PIN_D0 in this case) of |
|||
the master is connected to the SS pin of the slave/ |
|||
<b>SPI Mode Numbers</b> |
|||
#include <18f4520.h> |
|||
#fuses HS,XT,NOLVP,NOWDT,NOPROTECT |
|||
#use delay(clock=20000000) |
|||
There are four SPI "modes" which describe the relationship between the phase of the clock line and the phase of the MISO/MOSI lines. In order to successfully communicate using SPI, the master and slave must operate using the same mode. [http://en.wikipedia.org/wiki/Serial_Peripheral_Interface_Bus#Mode_Numbers Wikipedia] has a good explanation of these mode numbers. |
|||
void main(){ |
|||
int value; //for 8 bit transfer which is default |
|||
int value_read; // to be read from spi |
|||
<b>The Slave Select Line</b> |
|||
setup_spi(SPI_MASTER|SPI_H_TO_L|SPI_CLK_DIV_16); // sets the PIC as a master which generates the clock signal, a slower clock can be generated by changing 16 to 64 |
|||
In addition to being used to select which slave is active during a communication, the slave select (SS) line, is vital for synchronization. A slave's SS line should be high except when the master is talking to it. When the SS line is pulled low, the slave knows that a new communication is starting. |
|||
while (TRUE) { |
|||
value = 4; // value to be sent to spi |
|||
If a slave fails to receive a message properly, it will be reset when the SS line goes high at the end of the message and the slave will be prepared for a new message when the SS line goes low again. If the SS line is not used (e.g. by disabling it or running it directly to ground) there is a risk of the slave becoming out of sync with the master; if the slave misses a bit, it will always be one bit off in the future. Effective use of the SS line allows the slave and master to realign themselves at the beginning of each communication, in case of a transmission error. |
|||
//output_low(PIN_D0); //Turns on slave if slave select is used |
|||
spi_write(value); |
|||
value_read = spi_read(); |
|||
delay_us(100); |
|||
// output_high(PIN_D0); //Turns off slave if slave select is used |
|||
} |
|||
<b>Master Code:</b> |
|||
} |
|||
<pre>#include <18f4520.h> |
|||
#fuses EC,NOLVP,NOWDT,NOPROTECT |
|||
#device ADC=8 |
|||
#use delay(clock=40000000) |
|||
//These define the different SPI modes in terms of constants the compiler knows about |
|||
/*SLAVE CODE*/ |
|||
//NOTE: our PICs only seemed to work in modes 1 and 3, though they are supposed to work with any modes |
|||
#define SPI_MODE_0 (SPI_L_TO_H | SPI_XMIT_L_TO_H) |
|||
#define SPI_MODE_1 (SPI_L_TO_H) |
|||
#define SPI_MODE_2 (SPI_H_TO_L) |
|||
#define SPI_MODE_3 (SPI_H_TO_L | SPI_XMIT_L_TO_H) |
|||
#define SS_PIN PIN_D0 //this can be any output pin on the master |
|||
/* This code is valid only for devices that support hardware spi such as the PIC18F4520. For all other devices use |
|||
software spi. This code does not work on PIC18F4520 and will require more experimentation. This is the code that |
|||
looks most promising to start with from our review of codes on the internet for hardware SPI*/ |
|||
void main() |
|||
/* For hardware spi, library functions such as spi_setup(), spi_read() and spi_write() can be used */ |
|||
{ |
|||
/* For software spi, spi_xfer() can be used alongwith the #use spi directive*/ |
|||
int val; |
|||
/* We are not entirely sure if an output pin of the master should be connected to the SS pin (A5 for PIC18f4520) |
|||
of the slave */ |
|||
/* In most cases, simply grounding the SS pin of the slave should work */ |
|||
/* slave code is really hard to find on the internet */ |
|||
//Set up the ADC to read from A0 |
|||
#include <18f4520.h> |
|||
setup_adc_ports(AN0); |
|||
#fuses HS,XT,NOLVP,NOWDT,NOPROTECT |
|||
setup_adc(ADC_CLOCK_INTERNAL); |
|||
#use delay(clock=20000000) |
|||
set_adc_channel(0); |
|||
//The next statement sets up the SPI hardware, with this PIC as a master using mode 1 |
|||
void main(){ |
|||
//SPI_CLK_DIV_64 sets the speed of the SPI clock pulses--this is the slowest speed |
|||
setup_spi(SPI_MASTER | SPI_MODE_1 | SPI_CLK_DIV_64); |
|||
while(true) { |
|||
val = read_adc(); //read the value to be sent |
|||
output_low(SS_PIN); //pull the slave select line low to select the slave |
|||
delay_us(10); //give the slave time to notice this (may be unnecessary) |
|||
spi_write(val); //send the value |
|||
delay_us(10); //(may be unnecessary) |
|||
output_high(SS_PIN); //deselect the slave. |
|||
delay_ms(10); |
|||
} |
|||
}</pre> |
|||
int value_read; // to be read from spi |
|||
<b>Slave Code:</b> |
|||
setup_spi(SPI_SLAVE|SPI_H_TO_L); // sets the PIC as a slave which recieves a clock signal and data is transferred during H to L |
|||
<pre>#include <18f4520.h> |
|||
#fuses EC,NOLVP,NOWDT,NOPROTECT |
|||
#use delay(clock=40000000) |
|||
#include <LCD.C> |
|||
while (TRUE) { |
|||
value_read = spi_read(); // according to the reference manual this should work but also see the code below |
|||
#define SPI_MODE_0 (SPI_L_TO_H | SPI_XMIT_L_TO_H) |
|||
// if(spi_data_is_in()) // Checks to see if data is ready to be read |
|||
#define SPI_MODE_1 (SPI_L_TO_H) |
|||
// value_read = spi_read(); //reads the data from spi |
|||
#define SPI_MODE_2 (SPI_H_TO_L) |
|||
#define SPI_MODE_3 (SPI_H_TO_L | SPI_XMIT_L_TO_H) |
|||
delay_us(100); |
|||
void main() |
|||
ouput_d(value_read); //display the number that has been read |
|||
{ |
|||
spi_write(0); // This is probably unnecessary |
|||
setup_spi(SPI_SLAVE | SPI_MODE_1); //set up SPI hardware as a slave in mode 1 |
|||
lcd_init(); |
|||
while(true) { |
|||
val = spi_read(0); //spi_read must be passed an argument. The argument value is sent |
|||
//back to the master whenever the master sends us a message again. |
|||
//This allows two-way communication, but here the master ignores |
|||
//whatever the slave sends back, so just send a 0. |
|||
//display the value read: |
|||
lcd_gotoxy(1, 1); |
|||
printf(lcd_putc, "Pot at: %u ", val); |
|||
} |
} |
||
}</pre> |
|||
<b>Alternative Slave Code, Using Interrupts:</b> |
|||
<pre>#include <18f4520.h> |
|||
#fuses EC,NOLVP,NOWDT,NOPROTECT |
|||
#use delay(clock=40000000) |
|||
#include <LCD.C> |
|||
#define SPI_MODE_0 (SPI_L_TO_H | SPI_XMIT_L_TO_H) |
|||
#define SPI_MODE_1 (SPI_L_TO_H) |
|||
#define SPI_MODE_2 (SPI_H_TO_L) |
|||
#define SPI_MODE_3 (SPI_H_TO_L | SPI_XMIT_L_TO_H) |
|||
#int_ssp //this interrupt will occur whenever we receive an SPI message (or an I2C message, actually) |
|||
void message_receieved() { |
|||
unsigned int val; |
|||
val = spi_read(); //When you don't pass an argument, spi_read just returns the most |
|||
//recently received SPI message. We can do this here because we know |
|||
//that we just received a new message. |
|||
//display the value: |
|||
lcd_gotoxy(1, 1); |
|||
printf(lcd_putc, "Pot at: %u ", val); |
|||
} |
} |
||
void main() |
|||
{ |
|||
setup_spi(SPI_SLAVE | SPI_MODE_1); //setup the PIC as a slave in mode 1 |
|||
enable_interrupts(INT_SSP); //enable interrupts on SPI messages |
|||
enable_interrupts(GLOBAL); |
|||
lcd_init(); |
|||
while(true); //sit and wait for a message |
|||
}</pre> |
|||
<b>Two-way communication [This code has not been successfully tested!]</b> |
|||
Though not shown above, it is possible to use SPI for two-way communication between master and slave. If the slave calls |
|||
<pre>message_from_master = spi_read(123);</pre> |
|||
It will wait for the master to send some clock pulses; when the clock pulses come the slave will simultaneously read the message being sent by the master and send its own message (the number 123). The master, for its part, will call |
|||
<pre>message_from_slave = spi_read(22);</pre> |
|||
This will cause the master to send the number 22 to the slave and simultaneously read the message sent back by the slave (in this case the number 123). So the master and slave have exchanged one byte of information. If you are the master and you just want to get a byte from the slave without sending one, just send a junk value, which the slave will discard: |
|||
<pre>message_from_slave = spi_read(0);</pre> |
|||
<b>Useful resources</b> |
|||
The CCS user manual |
|||
[http://www.ccsinfo.com/forum/ CCS forum] |
|||
[http://en.wikipedia.org/wiki/Serial_Peripheral_Interface_Bus Wikipedia article] |
|||
/* WATCH OUT FOR UPDATES */ |
|||
</pre> |
Latest revision as of 01:53, 5 May 2009
Overview
SPI or Serial Peripheral Interface is a communication method that was once used to connect devices such as printers, cameras, scanners, etc. to a desktop computer. This function has largely been taken over by USB, but SPI can still be a useful communication tool for some applications. SPI runs using a master/slave set-up and can run in full duplex mode, meaning that signals can be transmitted between the master and the slave simultaneously. There is no standard communication protocol for SPI.
SPI is still used to control some peripheral devices and has some advantages over I2C (another type of serial data communication). SPI can communicate at much higher data rates than I2C. Furthermore, when multiple slaves are present, SPI requires no addressing to differentiate between these slaves. Compared to parallel buses, SPI has the additional benefit of requiring only simple wiring.
Peripheral devices that still use SPI:
• Converters (ADC and DAC)
• Memories (EEPROM and FLASH)
• Real Time Clocks (RTC)
• Sensors (temperature, pressure, etc.)
• Others (signal mixer, potentiometer, LCD controller, UART, CAN controller, USB controller, amplifier)
Basic Operation
SPI requires four lines, and is therefore often termed the “four wire” serial bus. These four lines are described in the table below.
Line | Name | Description |
---|---|---|
SCLK | Serial Clock | Output from master |
MOSI/SIMO | Master Output, Slave Input | Output from master |
MISO/SOMI | Master Input, Slave Output | Output from slave |
SS | Slave Select | Output from master (active low) |
The master, as its name suggests, controls all communication. By controlling the clock, the master decides when data is sent and received. Within each clock cycle a full duplex communication is carried out; each side sends and receives one bit of information. Because there is no standard communication protocol, the master can either send data or both send and receive data, depending on the needs of the application. Likewise, the slave can either receive data or both receive and send data back to the master.
Using the “Slave Select” line, the master chooses which slave with which to communicate. Note that more than one slave may be selected, simply by applying a logic low to the desired SS lines, as illustrated in the schematic diagram shown above. If a given slave is not selected (its SS is high) it disregards signals sent by the master.
References
SPI Background(www.totalphase.com)
SPI Wikipedia Article (www.wikipedia.org)
More Information (www.mct.net)
Circuit
Master connected to three slaves:
Here each slave must be enabled through the slave select pin in order to communicate with the Master.
One PIC master and one PIC slave:
The master, on the left, and the slave, on the right, are connected as shown above. Be sure you also connect grounds!
Code
SPI communication on the PIC is accomplished via the functions spi_read() and spi_write(), which read and write one byte at a time over SPI. In the example below, the master reads an 8-bit value from the analog-to-digital converter and sends it to the slave via SPI. The slave reads the value and displays it on an LCD display.
Note: The code given below does not work perfectly: the slave failed to received 10-20% of the messages sent by the master. In this example that failure rate was not critical, as the slave just missed an update of the ADC reading when it missed a message. If more accurate data transmission is required, the failure rate inherent in this system may be unacceptable.
SPI Mode Numbers
There are four SPI "modes" which describe the relationship between the phase of the clock line and the phase of the MISO/MOSI lines. In order to successfully communicate using SPI, the master and slave must operate using the same mode. Wikipedia has a good explanation of these mode numbers.
The Slave Select Line
In addition to being used to select which slave is active during a communication, the slave select (SS) line, is vital for synchronization. A slave's SS line should be high except when the master is talking to it. When the SS line is pulled low, the slave knows that a new communication is starting.
If a slave fails to receive a message properly, it will be reset when the SS line goes high at the end of the message and the slave will be prepared for a new message when the SS line goes low again. If the SS line is not used (e.g. by disabling it or running it directly to ground) there is a risk of the slave becoming out of sync with the master; if the slave misses a bit, it will always be one bit off in the future. Effective use of the SS line allows the slave and master to realign themselves at the beginning of each communication, in case of a transmission error.
Master Code:
#include <18f4520.h> #fuses EC,NOLVP,NOWDT,NOPROTECT #device ADC=8 #use delay(clock=40000000) //These define the different SPI modes in terms of constants the compiler knows about //NOTE: our PICs only seemed to work in modes 1 and 3, though they are supposed to work with any modes #define SPI_MODE_0 (SPI_L_TO_H | SPI_XMIT_L_TO_H) #define SPI_MODE_1 (SPI_L_TO_H) #define SPI_MODE_2 (SPI_H_TO_L) #define SPI_MODE_3 (SPI_H_TO_L | SPI_XMIT_L_TO_H) #define SS_PIN PIN_D0 //this can be any output pin on the master void main() { int val; //Set up the ADC to read from A0 setup_adc_ports(AN0); setup_adc(ADC_CLOCK_INTERNAL); set_adc_channel(0); //The next statement sets up the SPI hardware, with this PIC as a master using mode 1 //SPI_CLK_DIV_64 sets the speed of the SPI clock pulses--this is the slowest speed setup_spi(SPI_MASTER | SPI_MODE_1 | SPI_CLK_DIV_64); while(true) { val = read_adc(); //read the value to be sent output_low(SS_PIN); //pull the slave select line low to select the slave delay_us(10); //give the slave time to notice this (may be unnecessary) spi_write(val); //send the value delay_us(10); //(may be unnecessary) output_high(SS_PIN); //deselect the slave. delay_ms(10); } }
Slave Code:
#include <18f4520.h> #fuses EC,NOLVP,NOWDT,NOPROTECT #use delay(clock=40000000) #include <LCD.C> #define SPI_MODE_0 (SPI_L_TO_H | SPI_XMIT_L_TO_H) #define SPI_MODE_1 (SPI_L_TO_H) #define SPI_MODE_2 (SPI_H_TO_L) #define SPI_MODE_3 (SPI_H_TO_L | SPI_XMIT_L_TO_H) void main() { setup_spi(SPI_SLAVE | SPI_MODE_1); //set up SPI hardware as a slave in mode 1 lcd_init(); while(true) { val = spi_read(0); //spi_read must be passed an argument. The argument value is sent //back to the master whenever the master sends us a message again. //This allows two-way communication, but here the master ignores //whatever the slave sends back, so just send a 0. //display the value read: lcd_gotoxy(1, 1); printf(lcd_putc, "Pot at: %u ", val); } }
Alternative Slave Code, Using Interrupts:
#include <18f4520.h> #fuses EC,NOLVP,NOWDT,NOPROTECT #use delay(clock=40000000) #include <LCD.C> #define SPI_MODE_0 (SPI_L_TO_H | SPI_XMIT_L_TO_H) #define SPI_MODE_1 (SPI_L_TO_H) #define SPI_MODE_2 (SPI_H_TO_L) #define SPI_MODE_3 (SPI_H_TO_L | SPI_XMIT_L_TO_H) #int_ssp //this interrupt will occur whenever we receive an SPI message (or an I2C message, actually) void message_receieved() { unsigned int val; val = spi_read(); //When you don't pass an argument, spi_read just returns the most //recently received SPI message. We can do this here because we know //that we just received a new message. //display the value: lcd_gotoxy(1, 1); printf(lcd_putc, "Pot at: %u ", val); } void main() { setup_spi(SPI_SLAVE | SPI_MODE_1); //setup the PIC as a slave in mode 1 enable_interrupts(INT_SSP); //enable interrupts on SPI messages enable_interrupts(GLOBAL); lcd_init(); while(true); //sit and wait for a message }
Two-way communication [This code has not been successfully tested!]
Though not shown above, it is possible to use SPI for two-way communication between master and slave. If the slave calls
message_from_master = spi_read(123);
It will wait for the master to send some clock pulses; when the clock pulses come the slave will simultaneously read the message being sent by the master and send its own message (the number 123). The master, for its part, will call
message_from_slave = spi_read(22);
This will cause the master to send the number 22 to the slave and simultaneously read the message sent back by the slave (in this case the number 123). So the master and slave have exchanged one byte of information. If you are the master and you just want to get a byte from the slave without sending one, just send a junk value, which the slave will discard:
message_from_slave = spi_read(0);
Useful resources
The CCS user manual