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 CCS Technical Support

SPI Master Slave Problem w/ 876's

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



Joined: 01 Jun 2017
Posts: 4

View user's profile Send private message Send e-mail

SPI Master Slave Problem w/ 876's
PostPosted: Thu Jun 01, 2017 4:42 pm     Reply with quote

Hello, I have been fighting this problem for a couple of days and was wondering if you can help. I am using the latest Demo Version with 3 days left and using SPI (to) Master / Slave with two PIC16F876's. The slave has a 555 timer hooked to CCP1 with a 1kHz square and it reads the correct value to send to Master, but Master always gives me incorrect value. The wiring, pinouts and clock signals all check out so I figured there is something wrong with my code.
MASTER
Code:

#include <HWspiMaster.h>
#use spi (MASTER, SPI1, ENABLE=PIN_A5, BAUD=9600, MODE=0, BITS=16, STREAM=SPI_STREAM)
#define CLRuart() while(kbhit())ch=getc()

void main()
{
   unsigned long value;
   //byte lowbyte;
   //byte highbyte;
   char ch;
   printf("SPI Test\r\n");
   delay_ms(500);
   port_b_pullups(TRUE);
   printf("IPL Complete\r\n");
   delay_ms(1000);

   while(TRUE)
   {
   
     if(kbhit())
     ch=getch();
     ch=tolower(ch);
     if(ch=='t')
     {
     value=spi_xfer(SPI_STREAM,0x00,16);
     printf("Freq = %lu\r\n",value);
      } // ends if
   CLRuart();
   ch='&';
   delay_ms(1000);
   } // ends while

}

SLAVE
Code:

#include <HWspiSlave.h>
#use spi (SLAVE, SPI1, ENABLE=PIN_A5, MODE=0, BITS=16, STREAM=SPI_1)
#include "count.c"

unsigned long result;

#INT_SSP
void  SSP_isr(void)
{
printf("Data sent = %lu\r\n",result);
spi_xfer(SPI_1,result,16);
}

#INT_CCP1
void  CCP1_isr(void)
{
;
}

void main()
{

   enable_interrupts(INT_SSP);
   enable_interrupts(GLOBAL);
   result=0;
   printf("IPL Complete\r\n");


   while(TRUE)
       result = count(PIN_C2, 1000);   // count period = 1000 ms


Do you see any blatant errors? Thank you in advance. 'Chard
temtronic



Joined: 01 Jul 2010
Posts: 9253
Location: Greensville,Ontario

View user's profile Send private message

PostPosted: Thu Jun 01, 2017 6:07 pm     Reply with quote

I'd get rid of the printf inside the ISR in the Slave code, recompile, retest, report back .......


Jay
Ttelmah



Joined: 11 Mar 2010
Posts: 19561

View user's profile Send private message

PostPosted: Fri Jun 02, 2017 1:45 am     Reply with quote

Also you need to understand SPI....

Your hardware only transfers 8bits at a time (though the compiler will attempt to handle 16bits for you, there are still difficulties about this).

On the slave, the data to send has to be loaded into the buffer _before_ the master requests it. The master then receives the byte that is loaded, when it sends it's first byte, and the slave has to load the second byte before the master tries to transfer the next byte. This is the first problem about 8bit. The master needs to pause long enough between byte #1, and byte #2, for the slave to load the second byte. using the combined 16bit transfer at the master, doesn't allow such time.....
Then you have the problem of needing to load the data 'in advance'. This is why normally the master would transmit a byte, _before_ trying to receive. This then triggers the interrupt, allowing the slave to 'pre-load' the first byte.
The slave interrupt is 'per byte' (on your chip), and the compiler can't change this.

You don't show us your clock rates. Generally it'll take about 40 instructions for the slave to have loaded a byte ready to transfer.

Look at the example. EX_SPI_SLAVE.C
Understand that the slave has to read the byte that has arrived in the interrupt, and then if it is doing a write, load the next byte to send. This is the 'spi_prewrite()' function. You can get rid of the address handling in this, but should still have the single byte command to trigger the slave to load the value to send.
Chard



Joined: 01 Jun 2017
Posts: 4

View user's profile Send private message Send e-mail

PostPosted: Fri Jun 02, 2017 3:41 am     Reply with quote

Thank you for the expedient reply,Ttelmah and temtronic. I removed the printf from the ISR with same results. When this triggers it should send the
result that has been aquired, yes? spi_xfer(SPI_1,result,16);
Whether the result is 0x3e8 ( 1000 hz ) or even zero (if thats what the data is). I just get random values. The master code uses
Code:
value=spi_xfer(SPI_1, 0x00, 16);

Which sends a zero to trigger the slave ISR. Am I correct in assuming this?
My clock speeds are Master 14.31818 Mhz and Slave 4.096 Mhz which are the only two crystals I had left for these 876's. I will look at the example and try some 8 bit transfers (again). Gracias Mucho, Amigos, for the advice.
Ttelmah



Joined: 11 Mar 2010
Posts: 19561

View user's profile Send private message

PostPosted: Fri Jun 02, 2017 4:25 am     Reply with quote

Slave at about 1/4 the speed of the master. The master is going to have to pause for about 40uSec between asking for byte 1 and byte 2. You need to think again about your transfer. So something like:

Master issues a dummy byte.
Waits 50uSec.

This triggers the slave interrupt, and the reads the dummy byte, and then loads the first byte to send (understand what I said. The _hardware_ only supports 8 bits at a time).

Master clocks out a second byte, and receives the first byte from the slave. This also triggers a second slave interrupt. The slave again reads the byte, and loads the second byte to send. Master again waits for this to happen.

Master clocks out third byte, and receives the second byte.

So something like:
Code:

//SLAVE
unsigned long result;

#INT_SSP  //This is always a _byte_ transaction
void  SSP_isr(void)
{
   static int8 doing=0;
   int8 dummy;
   //slave reads the byte it has just been sent
   dummy=spi_xfer_in(SPI_1,0,8);

   if (doing==0)
   {
      spi_prewrite(SPI_1,make8(result,1)); //Load LSB
      if (dummy==0) //master has sent '0' marking this as a 'start'
          doing=1;
   }
   else if (doing==1)
   {
       spi_prewrite(SPI_1,make8(result,0),8); //load 8bits LSB
       doing=2;
   }
   else if (doing==2)
   {
       spi_prewrite(SPI_1,1); //dummy
       doing=0;
   }
}

//MASTER

int16 get_value(void)
{
    int16 temp;
    int8 dummy;
    //Now need to trigger the transfer
    dummy=spi_xfer(SPI_1,0,8); //The '0' marks a start
    //wait for the slave to load the first byte
    delay_us(50);
    dummy=spi_xfer(SPI_1,1,8); //clock the first byte back
    //this automatically loads the second byte
    delay_us(50);
    temp=make16(dummy,spi_xfer(SPI_1,1,0); //and the second
    return temp;
}


Key points to understand:

1) The hardware only supports 8bit transfers. Doesn't matter for the master, you can 'string' together multiple transfers (which is what the compiler does), but does for the slave. PIC24's and 32's do support 16bit hardware transfers. Your chip doesn't.
2) You don't get back what the slave has just loaded. You get back the _last_ byte the slave loaded. Hence you need to trigger the transfer by sending a dummy byte. Though data is sent both ways at once , the data you receive is always a 'reply'.
3) The problem of time. Because of the response nature, the slave has to have time to load it's reply, _before_ you ask for the next byte. The high speed 'strung together ' 16 or 32bit transfers are for use with _hardware_ where the data can all be ready to reply. Especially because your slave is so slow, you need to allow for this.

You also need to consider what happens if the slave changes the 'result' while a transfer is taking place. You need to handle this.
Chard



Joined: 01 Jun 2017
Posts: 4

View user's profile Send private message Send e-mail

PostPosted: Fri Jun 02, 2017 8:42 am     Reply with quote

Thank you Ttelmah for this explanation. I will give your sample code a try
and keep messing with it until this trial version runs out. I will either just rewrite this (all) in Hitech C or MikroC and give that a try for this 8 channel Cable/Capacitance tester. May even use two 887's and the PSP port for transfer if I keep getting grief like this. I just want to get this Finished !
You know, us Gemini's are short on patience....Har ! ( No....DOUBLE Har ! )
Thanks again, Ttelmah. 'Chard
Ttelmah



Joined: 11 Mar 2010
Posts: 19561

View user's profile Send private message

PostPosted: Fri Jun 02, 2017 8:56 am     Reply with quote

Seriously CCS C, is by far the easiest C to use on the market.

However you do need to understand what the hardware can do.
Chard



Joined: 01 Jun 2017
Posts: 4

View user's profile Send private message Send e-mail

PostPosted: Fri Jun 02, 2017 12:08 pm     Reply with quote

Yes, I agree its a great product and definitely on my hit list, but this
was a bit misleading
result = spi_xfer(stream, data, bits)
I was under the impression that this function did all the legwork to
acquire 16 bits if that was the parameter passed to it. The manual made no
reference to making multiple calls for the result, much less the data type
returned, which is why I declared an unsigned long when asking for 16 bits
in the stream. I did do some scouring on the web but a lot of information I got was a bit outdated, along with compiler versions. That is probably why I interpreted this function the way I did.
Anyway, enough "Hamming and Hawing", I appreciate your efforts and responses.
Ttelmah



Joined: 11 Mar 2010
Posts: 19561

View user's profile Send private message

PostPosted: Fri Jun 02, 2017 1:04 pm     Reply with quote

It does.
For the master, talking to SPI hardware, where the hardware has the data 'preloaded' into a 16bit register you can clock the data from. In other words, most SPI peripheral chips.

Problem is you are talking to a slave, that only has an 8bit register.
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