Professional Documents
Culture Documents
(GOOD) SPI Communications
(GOOD) SPI Communications
SPI Communications
ECE 376
SPI Communications
Goal:
Send and receive serial data using SPI protocol.
Why:
Efficiency. You can send an unlimited amount of data using 3+ lines: CLK: Clocks the serial data in and out SDO SDI SS Serial Data Out. Serial Data In Slave Select. SS enabled is required for the slave to drive its SDO line.
How: Hardware
Pick one processor to serve as the Master. This device drives the clock line The other devices serve as slaves. These devices also send data on the SDO line and receive on their SDI line, but, they only do so as commanded by the bus master. The master determines when the data is sent (SCK) and which slave is allowed to talk on the slave SDO line (SS). You usually need a separate SS (slave select) line for each slave device.
SS1
SS2
SS
JSG
NDSU
SPI - Master Mode
SPI Communications
ECE 376
Timing:
The master controls the clock line and pulses it once for each bit that is sent. You can adjust the clock's phase and sign using CKP and CKE. Data is sent out on the SDO line most significant bit first. At the same time, data is read in on the SDI line, most significant bit first. After eight bits are sent (one byte), SSPIF is set, signifying that the SPI port is ready to be read to see what data the slave sent, and ready to send a new byte.
JSG
NDSU
How: Software:
1. Set up PORTC as follows:
SPI Communications
ECE 376
1 -
0 -
2. Set up the conditions for the interrupt SSPSTAT (address 0x94 - Bank 1) Bit Name Value SMP: Sample bit: SPI Master Mode
1 = Input data sampled at end of data output time 0 = Input data sampled at middle of data output time
7 SMP 0
6 CKE 1
5 D/A x
4 P x
3 S x
2 R/W x
1 UA x
0 BF 0
CKP = 1
1 = Data transmitted on falling edge of SCK 0 = Data transmitted on rising edge of SCK
BF: Buffer Full Status bit Receive (SPI and I2C modes)
1 = Receive complete, SSPBUF is full 0 = Receive not complete, SSPBUF is empty
JSG
NDSU
Bit Name Value 7 0 6 0
SPI Communications
ECE 376
SSPCON (address 0x14 - Bank 0) 5 1 4 CKP 0 3 0 2 0 1 a 0 b WCOL SSPOV SSPEN SPI Master Clock Freq
Slave Mode:
1 = SSPBUF register is written while still transmitting the previous word (must be cleared in software) 0 = No collision
SSPOV: Receive Overflow Indicator bit 1 = A new byte is received while SSPBUF holds previous data. Data in SSPSR is lost on overflow.
In slave mode the user must read the SSPBUF, even if only transmitting data, to avoid overflows. In master mode the overflow bit is not set since each operation is initiated by writing to the SSPBUF register. (Must be cleared in software).
0 = No overflow SSPEN: Synchronous Serial Port Enable bit. In SPI mode, when enabled, these pins must be properly configured as input or output. 1 = Enables serial port and configures SCK, SDO, SDI, and SS as the source of the serial port pins 0 = Disables serial port and configures these pins as I/O port pins CKP: Clock Polarity Select bit 1 = Idle state for clock is a high level 0 = Idle state for clock is a low level bit 3-0: SSPM3:SSPM0: Synchronous Serial Port Mode Select bits 0000 = SPI master mode, clock = FOSC/4 0001 = SPI master mode, clock = FOSC/16 0010 = SPI master mode, clock = FOSC/64 0011 = SPI master mode, clock = TMR2 output/2 0100 = SPI slave mode, clock = SCK pin. SS pin control enabled. 0101 = SPI slave mode, clock = SCK pin. SS pin control disabled. SS can be used as I/O pin
Note that SPI communications is very fast: up to 5 million bits / second being transferred each way.
JSG
NDSU
3. Enable an INT interrupt SSPIE = 1
SPI Communications
ECE 376
At this point, you're ready to send and receive 8 bits of data using the on-board SPI port.
JSG
NDSU
Example: Write a routine which Passes 8 bits on SDO and Returns the 8-bits received on SDI
SPI Communications
ECE 376
Determine how long it takes to send data at the maximum bit rate Calling Format:
Result = SPI_RW('I'); // 'I' is sent on SDO // the data from SDI is // returned in Result
Subroutines:
void Init_SPI(void) { SSPCON = 0x20; TRISC5 = 0; TRISC4 = 1; TRISC3 = 0; SSPIE = 0; }
unsigned char SPI_RW(unsigned char Data) { unsigned char Result; SSPIF = 0; SSPBUF = Data; while (!SSPIF); Result = SSPBUF; return(Result); }
// load the SPI data buffer // wait until data sent // load what the slave sent // and return it
SPI communication is a convenient and fast way to send data from one chip to another on the same circuit board.
JSG
NDSU
SPI - Slave
SPI Communications
ECE 376
SPI slave mode is just like the master mode, save the clock line is driven by someone else (CLK = input for slaves). If using more than one slave, tie the slave select lines from the master to RA5. This causes the slave to ignore the data unless SS=0. It also synchronizes the data to the falling edge of SS.
Note: Since you can't control how fast the data is sent, the slave often times Uses an interrupt to receive each byte (you don't have time to wait since the next byte may be coming within the next 1.6us) Save each byte in a stack for temporary storage (you don't have time to decode the data as it comes in. The next byte may be coming in the next 1.6us). The main routine can then figure out what the data means and what it's supposed to to with this data once it's been received.
Timing:
JSG
NDSU
How: Software:
1. Set up PORTC as follows:
SPI Communications
ECE 376
1 -
0 -
2. Set up the conditions for the interrupt SSPCON (address 0x14 - Bank 0) 6 5 4 3 CKP 0 0 0 1
7 0
2 1
1 0
0 a
SPI Slave
3. Enable an SPI interrupt (The slave really needs to use interrupts since it can't control how fast the data comes in. The slave needs to read the data and save it as fast as possible.) SSPIE = 1
JSG
NDSU
Example Code:
1. Initialization Routine for SPI Slaves:
void Init_SPI_Slave(void) { TRISA5 = 1; SSPCON = 0x25; SSPIF = 0; SSPIE = 1; PEIE = 1; GIE = 1; }
SPI Communications
ECE 376
2. Interrupt Service Routine: In this example, load the data which is incoming into a buffer, SPI_STACK, as it is read in. Assume that no data is sent back to the master.
Interrupt Service Routine: Read the SPI port and place the data into a circular stack of six
// Global unsigned char Stack_Pointer; unsigned char SPI_Stack[6]; // Interrupt Service Routine void interrupt IntSer(void) @ 0x10 { if (SSPIF == 1) { SPI_Stack[Stack_Pointer] = SSPBUF; Stack_Pointer += 1; Stack_Pointer = Stack_Pointer % 6; // // SSPBUF = DATA;
// got a byte
SPIF = 0; } }
JSG
NDSU
SPI Communications
ECE 376
Example: DS1267 Digital Potentiometer: Write a routine which sets the value of a DS1267 digital potentiometer. Make the calling function
void Set_Pot(unsigned char A, unsigned char B)
A where A and B are the values of the two potentiometers: R = R 0 255 .
Step 2. Set up the hardware connections. Connect DQ to the SPI data line (SDO) CLK to the SPI clock line (SCK) RST to some other pin on the PIC chip (sort of a slave select line.) Assume RC0 for now.
Step 3. In the data sheets, find a timing diagram. This is given in Figure 9 and Figure 1:
JSG
10
NDSU
SPI Communications
ECE 376
From these diagrams, it appears you need to... Send 24 bits (17 bits, rounded up to a multiple of eight) This data is shifted through a shift register on the DS1267 so that you can cascade several digital pots. The last 17-bits send are the ones that matter. These 24-bits should look like the following: Flip the bits in each byte. The data bits are received LSB first for the DS1267 while the PIC sends the data MSB first. Either write your own SPI routine or write a flip routine. First byte sent x x x x x x x padding bits and Stack Select Second Byte Sent Pot 1 value - LSB first Third Byte Sent Pot #2 value - LSB first
0 b0 b1 b2 b3 b4 b5 b6 b7 b0 b1 b2 b3 b4 b5 b6 b7
Step 4. Write some code: Option #1: Use the On-Chip SPI routine. Assume Y=flip(X) flips all bits:
void Set_Pot(unsigned char A; unsigned char B) { static bit RST @ ((unsigned)&PORTC*8+0); // RST = RC0 RST = 0; RST = 1; SSPIF = 0; SSPBUF = 0; while (!SSPIF); SSPIF = 0; SSPBUF = flip(A); while (!SSPIF); SSPIF = 0; SSPBUF = flip(B); while (!SSPIF); RST = 0; }
JSG 11 rev April 18, 2005
NDSU
SPI Communications
ECE 376
void Set_Pot(unsigned char A; unsigned char B) { unsigned char i; RST = 0; SCK = 0; RST = 1; DQ = 0; SCK = 1; // default levels for RST // and clock // select the DS1267 // Stack select = 0 // pulse the clock
SCK = 0;
for (i=0; i<8; i++) { if (((A>>i) & 1) == 0) DQ = 0; else DQ = 1; SCK = 1; SCK = 0; } for (i=0; i<8; i++) { if (((B>>i) & 1) == 0) DQ = 0; else DQ = 1; SCK = 1; SCK = 0; } RST = 0; }
JSG
12
NDSU
What can you do with a digital pot?
SPI Communications
ECE 376
1) Design an amplifier which the PIC can monitor and adjust. The PIC monitors the amplitude of the signal at RA0. If it is too small, increases the gain (increase R). If it is too large and is saturating the amplifier (RA0 too close tp +5V), reduce the gain (reduce R).
Amplified Signal
2) Design a tunable low-pass filter. Set the corner from 100Hz to 1kHz. By adjusting R, you set the corner frequency. A 1-stage filter is shown on the left. A 3-stage Butterworth filter is shown on the right. All three R's should have the same value.
C2 0.0224 100k 100k 100k 100k
C
C1 0.0088 C3 0.0013
JSG
13
NDSU
2-Wire SPI Communications:
SPI Communications
ECE 376
Some companies, such as Dallas Semiconductors, use a 2-wire SPI communication scheme. This uses A clock line from the master (as a normal SPI communication scheme), but A single data line. The concept is that often times you only have communication going one way. If you constrain all communications to happen in one direction only (termed half duplex), you can save one data line: When the master is talking, it drives the data line and the slave listens. When the slave replies, the master switches its data line to input (high z) and the slave drives the data line. Since the data can go both ways, add a 1k resistor in-between as a buffer. This limits the current flow if the PIC and the DS1620 try to drive the data line at the same time. This should never happen, if the program is written properly. It doesn't hurt to be safe, though.
PIC16F876 (Master) RC3 DS1620 (Slave) DQ Vdd 2.7V to 5.5V
Data (R=1k)
Clock
RC2
CLK
Slave Select
RC1 RST
GND
Step 2. Find the hardware connections. Arbitrarily, let RC1: RST (1 = start comm. 0 = terminate) RC3: DQ (1k resistor between) RC2: CLK (pulse low = clock)
JSG
14
NDSU
SPI Communications
ECE 376
You Start with CLK = 1, RST = 0. Pull RST high to start communications Set DQ = output for the PIC to drive the data line. Clock out 8 bits of data with the data valid on the rising edge of the clock. This is the command for the DS1620 processor. If you are sending data to the DS1620, keep DQ as output. Clock out 8 more bits, LSB first. If you are receiving data, switch DQ to input. Clock in 9 bits of data, LSB first. Data valid on clock low (not obvious on the data sheets but found experimentally in lab)
Step 4. Find in the data sheets what commands you need to use to start a temperature conversion, read temperature, etc.
JSG
15
NDSU
Example:
SPI Communications
ECE 376
Write routines to Define the bits used in a more meaningful way that RC0... Send 8 bits to the DS1620 Receive 9 bits from the DS1620 Initialize the DS1620 to continuous temperature readings, and Read the temperature
NDSU
SPI Communications
ECE 376
// Start communications // with RST going high // x0C = Write Config Reg = 00 // (sets up continuous conv)
JSG
17