CCS C Software and Maintenance Offers
FAQFAQ   FAQForum Help   FAQOfficial CCS Support   SearchSearch  RegisterRegister 

ProfileProfile   Log in to check your private messagesLog in to check your private messages   Log inLog in 

CCS does not monitor this forum on a regular basis.

Please do not post bug reports on this forum. Send them to support@ccsinfo.com

SPI Master / Slave behaviour question

 
Post new topic   Reply to topic    CCS Forum Index -> General CCS C Discussion
View previous topic :: View next topic  
Author Message
melstav



Joined: 01 Mar 2006
Posts: 5

View user's profile Send private message

SPI Master / Slave behaviour question
PostPosted: Wed Mar 01, 2006 9:41 am     Reply with quote

I'm starting a project where I'd like to offload handling of certain I/O peripherals to a secondary PIC configured as an SPI slave.

I have a couple toy programs I wrote to deomonstrate the functionality of using a PIC as an SPI slave before I go actually tying it into useful code. Both programs use spi_read() to read and write to the SPI bus at the same time, and then output the ascii value of the byte that they received on the serial port.

My master program always sends 'X' (ascii 88) to the slave, which always displays 88 on the serial port. That works well. My slave, on the other hand, I'm expecting to always send a null character (ascii 0) back to the master. Instead, it's apparently alternating between sending a null and ascii 88.

Here's sample output from the master:
Quote:

Hello.

.0
.0
.88
.0
.88
.0
.88


Is this expected? I do not have any pull-up or pull down resistors on the SPI bus. SDO on one chip is wired directly to SDI on the other.

I find it a little disconcerting. It doesn't matter what value I configure the slave to send back, it always alternates with the value sent by the master.

I can forsee situations where in response to receiving a single byte from the master, the slave has to send back four or more bytes of data back.

Thanks for any input you can provide.
I'm using PCWH 3.243.

Barry

Here is the code for the SPI Master:
Code:

#include "D:\src\pic\random tests\spi_mstr\spi_mstr.h"
#case

void main() {
   char in_char;

   setup_adc_ports(NO_ANALOGS);
   setup_adc(ADC_OFF);
   setup_spi(SPI_MASTER|SPI_L_TO_H|SPI_CLK_DIV_4);
   setup_timer_0(RTCC_INTERNAL|RTCC_DIV_1);
   setup_timer_1(T1_DISABLED);
   setup_timer_2(T2_DISABLED,0,1);

   puts("Hello.\r\n");

   while (TRUE) {
      putc('.');
      in_char = spi_read('X');
      printf ("%d\r\n", in_char);
      delay_ms(500);
   }

}


And the code for the SPI Slave:
Code:

#include "D:\src\pic\random tests\spi_slave\spi_slave.h"
#case

/**********************************************************************
 *
 * Global Variables
 *
 **********************************************************************/

char spi_xmit_buffer = 0;
char spi_recv_buffer = 0;

/**********************************************************************
 *
 * Interrupt Service Routines
 *
 **********************************************************************/

#int_SSP
SSP_isr() {
   spi_recv_buffer = spi_read(spi_xmit_buffer);
   spi_xmit_buffer = 0;
}

/**********************************************************************
 *
 * Main Program
 *
 **********************************************************************/



void main() {

   setup_adc_ports(NO_ANALOGS);
   setup_adc(ADC_OFF);
   setup_spi(SPI_SLAVE|SPI_L_TO_H|SPI_SS_DISABLED);
   setup_timer_0(RTCC_INTERNAL|RTCC_DIV_1);
   setup_timer_1(T1_DISABLED);
   setup_timer_2(T2_DISABLED,0,1);
   enable_interrupts(INT_SSP);
   enable_interrupts(GLOBAL);

   while(TRUE) {
      if (spi_recv_buffer) {
         printf("%d\r\n", spi_recv_buffer);
         spi_recv_buffer = 0;
      }
   }
}


spi_mstr.h and spi_slave.h both look identical:
Code:

#include <16F876.h>
#device adc=8

#FUSES NOWDT                    //No Watch Dog Timer
#FUSES HS                       //High speed Osc (> 4mhz)
#FUSES NOPUT                    //No Power Up Timer
#FUSES NOPROTECT                //Code not protected from reading
#FUSES BROWNOUT                 //Reset when brownout detected
#FUSES LVP                      //Low Voltage Programming on B3(PIC16) or B5(PIC18)
#FUSES NOCPD                    //No EE protection
#FUSES NOWRT                    //Program memory not write protected
#FUSES NODEBUG                  //No Debug mode for ICD

#use delay(clock=3686400)
#use rs232(baud=38400,parity=N,xmit=PIN_C6,rcv=PIN_C7,bits=8)
Ttelmah
Guest







PostPosted: Wed Mar 01, 2006 10:27 am     Reply with quote

When you send a byte on SPI, a byte is clocked back at the same time (both transfers occur on the same clock). The byte clocked back, is whatever is sitting in the SPI register at the slave, when the clocks are sent. If you send an 88, _this_ will be sitting in the slaves SPI data register, unless it has been cleared. Now clearing the register, will take time at the slave. So what is happening, is that if you send another transaction reasonably quickly, you will see 'back', the data you sent in the last transaction (the 88...).
The CCS implementation of SPI, is weak (on transmit, it waits for the transaction to happen, rather than taking advantage of the hardware), and also the PIC itself, has some severe timing limits (the interrupt response latency is very high on the PIC, so as a slave, a fairly long delay is needed on the master, before a 'response' can be relied on...).
Basically, when you send a byte, you will receive back, whatever garbage is already present in the slaves SPI register. Only the second byte (after a pause), will contain legitimate data.
Implementing a good SPI slave system, will require that you are aware of this 'last response' behaviour, and of the timing overheads in the system as a whole.

Best Wishes
melstav



Joined: 01 Mar 2006
Posts: 5

View user's profile Send private message

PostPosted: Thu Mar 02, 2006 11:29 am     Reply with quote

Thanks. After reading your response, I went back and re-read the entry in hte helpfile for spi_read() and the SPI section of the datasheet for the chip I'm using, and I see why I was confused.

Here's a quote from the help entry:
Quote:

If a value is passed to SPI_READ the data will be clocked out and the data received will be returned. If no data is ready, SPI_READ will wait for the data.


I was expecting the byte that was passed to spi_read to be clocked out at the same time that the incoming byte was being clocked in.

HOWEVER, because I was calling spi_read() from the ISR, the incoming byte was already completely received by the time the function was called, so the byte was being loaded into the SSPBUF for the next byte to be clocked.

I modified my slave code so that instead of using a global variable to store the next byte to send out and passing it to spi_read(), I write the byte directly to the SSPBUF. It now behaves completely as expected.

Thanks for pointing me in the right direction.
Display posts from previous:   
Post new topic   Reply to topic    CCS Forum Index -> General CCS C Discussion All times are GMT - 6 Hours
Page 1 of 1

 
Jump to:  
You cannot post new topics in this forum
You cannot reply to topics in this forum
You cannot edit your posts in this forum
You cannot delete your posts in this forum
You cannot vote in polls in this forum


Powered by phpBB © 2001, 2005 phpBB Group