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 Help
Goto page 1, 2  Next
 
Post new topic   Reply to topic    CCS Forum Index -> General CCS C Discussion
View previous topic :: View next topic  
Author Message
carl



Joined: 06 Feb 2008
Posts: 240
Location: Chester

View user's profile Send private message

SPI Help
PostPosted: Mon Jul 23, 2012 3:53 am     Reply with quote

HI All,

I hope someone can help me. I am trying to communicate between two PIC's via SPI. I have tried creating a simplest test program - but it isn't working. It does Compile correctly.

Pre-requisites:
CCS Version: 4.038.
Both PIC's are 4550.
Oscillator is 48MHz.
PIC1# sends a variable. (Matser)
PIC2# reads variable and printf onto LCD (Slave). I have tried a different test program just for PIC#2 and the LCD - and it definetly works - so no issues with the LCD setup.

Connections:

MASTER SLAVE
SDO (PIN 26) ........> SDI (PIN 33)
SDI (PIN 33) <........ SDO (PIN 26)
CLK (PIN 34) ........> CLK (PIN 34)
SS (PIN 7) TIED TO GROUND ON SLAVE PIC
GND <....................> GND


Master Code
Code:
#include <18F4550.H>
#fuses HS, NOWDT, NOPROTECT, BROWNOUT, PUT, NOLVP
#use delay(clock=48000000)


#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)

#include "flex_lcd_16x1.c"

//============================
void main()
{
setup_spi(SPI_MASTER | SPI_MODE_0 | SPI_CLK_DIV_16);
SETUP_ADC_PORTS(NO_ANALOGS);

while(1)
  {
   spi_write(0xF);
   delay_ms(3000);
   spi_write(0x3);
   delay_ms(3000);
  }
}


Slave Code
Code:
#include <18F4550.H>
#fuses HS, NOWDT, NOPROTECT, BROWNOUT, PUT, NOLVP
#use delay(clock=48000000)


#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)

#include "flex_lcd_16x1.c"
int32 val;

void main()
{
   setup_spi(SPI_SLAVE | SPI_MODE_0); //set up SPI hardware as a slave in mode 0
   SETUP_ADC_PORTS(NO_ANALOGS);
   
   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:
       printf(lcd_putc,"Value: %Lu     ", val);
       delay_ms(1000);

   }
}


I checked the lines with a scope and there are signals on each of the lines. The clock frequency is 3MHz - whihch is correct 48/12 = 3.

I presueme it is something I am doing in the code incorrectly.
I have ensured that no analogues are set on the ADC ports.
RS232 is not enabled - because this would conflict with the Hardware SPI.

Any thoughts - or does anyone have a simple test program between two pics to just send and recieve a variable - then display on LCD?

Thanks in advance
Carl
ckielstra



Joined: 18 Mar 2004
Posts: 3680
Location: The Netherlands

View user's profile Send private message

PostPosted: Mon Jul 23, 2012 4:48 am     Reply with quote

A few things come to mind:
1) Version 4.038 is very old and a bad version. The first series of releases in the v4.0xx range were beta versions for testing the new compiler. Only from around 4.076 the compiler became usable again. Even when you get code working in 4.038 you will run into problems with future changes. Save yourself and everybody else a lot of time and get a version 4.076 or higher or downgrade to v3.249, the latest stable v3 compiler.

2) The values of 0xF and 0x3 you are sending are in the ASCII Control Characters range. Are you sure the LCD will display these values correctly?

3) You haven't implemented a Slave Select Line. Without this signal the master and slave will run out of sync. There is no way for the Slave to determine which bit is the start of a new byte. One induce noise spike on the clock line and the slave will receive different bytes than the master is sending...

About example code... This forum has a search function that will find you lots of examples.
Here is just one of them: http://www.ccsinfo.com/forum/viewtopic.php?t=39145
FvM



Joined: 27 Aug 2008
Posts: 2337
Location: Germany

View user's profile Send private message

PostPosted: Mon Jul 23, 2012 4:57 am     Reply with quote

The default slave mode requires slave select operation, but you apparently don't connect SS, or at least don't activate the select signal on the master side. SPI_SS_DISABLED must be specified to make the slave work without SS.

In practice, it's very difficult to achieve reliable SPI operation without select signal, as ckielstra mentioned, so you should better use it.

In addition, I don't know if this early CCS V4 version is working correctly.
carl



Joined: 06 Feb 2008
Posts: 240
Location: Chester

View user's profile Send private message

PostPosted: Mon Jul 23, 2012 5:19 am     Reply with quote

Thankyou both,

I am looking into it and will try a few things that you suggest.
carl



Joined: 06 Feb 2008
Posts: 240
Location: Chester

View user's profile Send private message

PostPosted: Mon Jul 23, 2012 5:46 am     Reply with quote

OK, I have added the SS Line. THe Master code is now:
Code:
#include <18F4550.H>
#fuses HS, NOWDT, NOPROTECT, BROWNOUT, PUT, NOLVP
#use delay(clock=48000000)


#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)

#include "flex_lcd_16x1.c"
#define SPI_SS   PIN_E2

//============================
void main()
{
output_high(SPI_SS);  // Initial Slave Select to a high level
setup_spi(SPI_MASTER | SPI_MODE_0 | SPI_CLK_DIV_16);
SETUP_ADC_PORTS(NO_ANALOGS);

while(1)
  {
   output_low(SPI_SS);
   spi_write(47);
   output_high(SPI_SS);
   delay_ms(500);
   output_low(SPI_SS);
   spi_write(300);
   output_high(SPI_SS);
   delay_ms(500);
  }
}


Regarding, the LCD Print F 'correct format' - I have just changed the value to an unsigned int. and the print f format to %u. I think this is correct. The slave software is here:
Code:
#include <18F4550.H>
#fuses HS, NOWDT, NOPROTECT, BROWNOUT, PUT, NOLVP
#use delay(clock=48000000)


#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)

#include "flex_lcd_16x1.c"
unsigned int val;

void main()
{
   setup_spi(SPI_SLAVE | SPI_MODE_0); //set up SPI hardware as a slave in mode 0
   SETUP_ADC_PORTS(NO_ANALOGS);
   
   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:
       printf(lcd_putc,"Value: %u     ", val);
       delay_ms(1000);

   }
}



Regarding your questions
Quote:
1) Version 4.038 is very old and a bad version. The first series of releases in the v4.0xx range were beta versions for testing the new compiler. Only from around 4.076 the compiler became usable again. Even when you get code working in 4.038 you will run into problems with future changes. Save yourself and everybody else a lot of time and get a version 4.076 or higher or downgrade to v3.249, the latest stable v3 compiler.

I agree, but I am stuck with it. I have to use this.

2) The values of 0xF and 0x3 you are sending are in the ASCII Control Characters range. Are you sure the LCD will display these values correctly?

I have changed them to a unsigned int, and believe I have used the correct format for printF?

3) You haven't implemented a Slave Select Line. Without this signal the master and slave will run out of sync. There is no way for the Slave to determine which bit is the start of a new byte. One induce noise spike on the clock line and the slave will receive different bytes than the master is sending...

This has now been added.
so pin10 (RE2) from the master to pin7 (RA5) on the slave.

About example code... This forum has a search function that will find you lots of examples.
Here is just one of them: http://www.ccsinfo.com/forum/viewtopic.php?t=39145

I have been looking and trying to use examples, but cannot find anything that is a very very simple test. I think my code is as minimalistic as you can get for SPI.


Anyway, I have tried all the above, and still no luck.
any further thoughts.

Thanks
Carl
carl



Joined: 06 Feb 2008
Posts: 240
Location: Chester

View user's profile Send private message

PostPosted: Mon Jul 23, 2012 6:34 am     Reply with quote

A bit of success.
Apologies for missing this.


The LCD program was taken from PCM programmer, and I did not include this line with the Slave porgram:

Quote:
// This macro converts the lower nybble of a byte to
// an ASCII hex character, so it can be displayed with
// lcd_putc().
#define tohex(x) (x & 0xF) < 10 ? x + '0' : x + '7'


As soon as this was put in, I am getting something on the display. I need to pay around with it a bit more.

Thanks for now.
Carl
temtronic



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

View user's profile Send private message

PostPosted: Mon Jul 23, 2012 7:12 am     Reply with quote

It might be a good idea to add a comment line in your programs at the top to say 'host program', 'slave program' or similar wording, that way everyone knows which program is which.
Also another comment like 'using Zigbee modules' would be helpful.

While you know what' is what now....a few weeks or even days from now, it won't be so clear !

hth
jay
carl



Joined: 06 Feb 2008
Posts: 240
Location: Chester

View user's profile Send private message

PostPosted: Mon Jul 23, 2012 7:17 am     Reply with quote

good point

I will, thanks
ckielstra



Joined: 18 Mar 2004
Posts: 3680
Location: The Netherlands

View user's profile Send private message

PostPosted: Mon Jul 23, 2012 7:46 am     Reply with quote

Code:
spi_write(300);
spi_write only accepts int8 as input. The value 300 is too large and will be truncated to the lowest 8 bits, i.e. a value of 55 or ASCII character '7' on your LCD.
carl



Joined: 06 Feb 2008
Posts: 240
Location: Chester

View user's profile Send private message

PostPosted: Mon Jul 23, 2012 7:54 am     Reply with quote

Hi ckielstra,

Yes just been playing around with that. Thanks for letting me know.
And I presume that if you need to send 32bits then you have to break it up into 4 bytes? - so do four SPI writes?

and then reassemble in the slave PIC.
temtronic



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

View user's profile Send private message

PostPosted: Mon Jul 23, 2012 1:58 pm     Reply with quote

Yup. The builtin SPI hardware is an 8 bit device, so you need 4 sends..and then 'build' the data in the right order...
carl



Joined: 06 Feb 2008
Posts: 240
Location: Chester

View user's profile Send private message

PostPosted: Tue Jul 24, 2012 2:23 am     Reply with quote

THanks Temtronic,

Before I start investigating a 32-bit transfer (actually it will probably be 24-bit - not finilised yet), I need to get the basics of SPI correct first.
I have incorporated another of PCM's programs which incorporates an interrupt driven slave.

The program works for most of the time, excpet now and again it gives incorrect values on the display.

the ISR when activated should should display 47 and then 255 - the values sent from the master. and when the ISR is not activated it should display 50.

What is happening is that it does display 50 correctly, and then when the ISR occurs it displays 255, then 47 - but sometimes it just displays '0'. This is not correct!! Why does it sometimes display the wrong value?

The Master code:
Code:
/////// MASTER PROGRAM ////////

#include <18F4550.H>
#fuses HS, NOWDT, NOPROTECT, BROWNOUT, PUT, NOLVP
#use delay(clock=48000000)


#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)

#include "flex_lcd_16x1.c"
#define SPI_SS   PIN_E2

//============================
void main()
{
output_high(SPI_SS);  // Initial Slave Select to a high level
setup_spi(SPI_MASTER | SPI_MODE_0 | SPI_CLK_DIV_16);
SETUP_ADC_PORTS(NO_ANALOGS);

while(1)
  {
   output_low(SPI_SS);
   spi_write(47);
   output_high(SPI_SS);
   delay_ms(5000);
   output_low(SPI_SS);
   spi_write(255);
   output_high(SPI_SS);
   delay_ms(5000);
  }
}


The slave code:
Code:

/////// SLAVE PROGRAM ////////

#include <18F4550.H>
#fuses HS, NOWDT, NOPROTECT, BROWNOUT, PUT, NOLVP
#use delay(clock=48000000)

#define tohex(x) (x & 0xF) < 10 ? x + '0' : x + '7'
#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)

#include "flex_lcd_16x1.c"
unsigned int val =50;

// Variables used for the SPI slave receive buffer.
#define BUFFER_SIZE  80
int8 buffer[BUFFER_SIZE];
int8 next_in = 0;
int8 next_out = 0;

#define spi_kbhit (next_in != next_out)

//----------------------------
// Create an interrupt-driven SPI receive
// buffer, similar to the way it's done for
// RS-232 in the CCS example file, EX_SISR.C.

#int_ssp
void ssp_isr(void)
{
buffer[next_in] = spi_read();
next_in++;
if(next_in >= BUFFER_SIZE)
   next_in = 0;
}

//----------------------------
// Wait for a character to become available
// in the spi slave's receive buffer.  Then
// get it from the buffer and return it.

int8 spi_bgetc(void)
{
int8 c;
while(!spi_kbhit);
c = buffer[next_out];
next_out++;
if(next_out >= BUFFER_SIZE)
   next_out = 0;     
return(c);
}

void main()
{
   
   unsigned int c;
   setup_spi(SPI_SLAVE | SPI_MODE_0); //set up SPI hardware as a slave in mode 0
   SETUP_ADC_PORTS(NO_ANALOGS);
   clear_interrupt(INT_SSP);
   enable_interrupts(INT_SSP);
   enable_interrupts(GLOBAL);
   while(true) {
 lcd_init();
 if(spi_kbhit)  // Char available from SPI slave ?
     {
      c = spi_bgetc();  // If so, get it
      printf(lcd_putc,"\f Value: %u     ", c);
      delay_ms(500);
     }
else
{
printf(lcd_putc,"\f Value: %u     ", val);
   }
  }
}


Any help much appreciated
carl



Joined: 06 Feb 2008
Posts: 240
Location: Chester

View user's profile Send private message

PostPosted: Tue Jul 24, 2012 3:13 am     Reply with quote

Hmmm....

Changed the mode from '0' to '1' and it seems to be working correctly.

BUT WHY?
Ttelmah



Joined: 11 Mar 2010
Posts: 19510

View user's profile Send private message

PostPosted: Tue Jul 24, 2012 5:12 am     Reply with quote

No guarantees.
Have put together a master, and slave, which shows how a master can send a variable sized 'block' of bytes, which the slave receives, and at the same time receive the reply back from the slave. Note particular things:
1) The reply byte has to be loaded in advance of the next transfer each time.
2) I use direct access to SSPBUF, since I want to make _sure_ the compiler does not wait when I load the byte.
3) The delays between bytes to ensure the slave has time to load the next byte.
Both parts are in the single file, with you needing to separate the master part, and add the header (common to both). Obviously change processor file and fuses to suit your hardware.
This does not use slave select, but _will_ be more likely to work with it. Obviously suitable slave line operation would need to be added to master.
Key things are the way that transfers in both directions happen at the same time, and how to handle different size packets.

Code:

#include <16F877A.h>
#FUSES NOWDT, HS, PUT, NOLVP
#use delay(clock=20000000)

#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)
#use rs232(UART1, baud=9600, ERRORS)
//Treat to here as processor header for master as well

//Slave code for a SSP interface that can TX/RX up to 16 bytes
#byte SPIBUF=getenv("SSPBUF") //Give direct access to SSP buffer
int8 no_bytes=0; //number of bytes to transfer
int8 ssprxbuffer[16]; //receive buffer
int8 ssptxbuffer[16]; //transmit buffer
int1 data_tx=FALSE; //flag to say that data is in transit
int8 sspctr=0; //byte counter
int1 transaction_complete=FALSE;

#INT_SSP
void ssp_has_data(void) {
  if (data_tx) {
     //already in a transaction
     ssprxbuffer[sspctr++]=SPIBUF; //receive byte
     if (sspctr>=no_bytes) { //transaction finished
        sspctr=0;
        data_tx=FALSE;
        transaction_complete=TRUE;
     }
     else {
        SPIBUF=ssptxbuffer[sspctr]; //else load next TX byte
     }   
  }
  else {
     data_tx=TRUE; //data to follow
     no_bytes=SPIBUF; //no of bytes to transfer
     sspctr=0; //should already be clear, but make sure
     SPIBUF=ssptxbuffer[sspctr]; //load first byte
  }
}

void main(void) {
  int8 ctr;
  union {
     float val;
     int8 bte[4];
  } rx_val;
  setup_spi(SPI_SLAVE | SPI_MODE_0 | SPI_SS_DISABLED);
  //load output buffer with a counter
  for (ctr=0;ctr<16;ctr++) {
     ssprxbuffer[ctr]=ctr;
  }
  do {
     
     //Wait for master to say what it wants
     if (transaction_complete) {
        transaction_complete=FALSE;
        //here master has sent something
        for (ctr=0;ctr<4;ctr++){
           rx_val.bte[ctr]=ssprxbuffer[ctr];
        }
        //now rx_val.val is a float transferred from the master
        rx_val.val/=3.141592654;
        //send back on the _next_ transaction the value/pi
        for (ctr=0;ctr<4;ctr++){
           ssprxbuffer[ctr]=rx_val.bte[ctr];
        }
     }
  } while(TRUE);     
}*/

//Now master code to talk to this
//----------------------------------------------------------------------------
//needs processor header as for slave here
int8 spi_read_with_pause(int8 x) {
   int8 rval;
   rval=spi_read(x);
   delay_us(20);
   return rval;
   //There has to be a delay between bytes to allow the next one to be loaded
   //at the slave. Allow something like 50 instruction times.
}

char * spi_transfer(char * buffer, int8 size) {
  //Perform bi-directional SPI transfer of 'size' bytes
  int8 ctr;
  static int8 rx_buffer[16];
  if (size>16) return 0; //maximum allowed transfer size.....
  spi_write(size); //tell the slave how many bytes to expect
  delay_us(20); //same comment as above on delay
  //Now send 'size' bytes
  for (ctr=0;ctr<size;ctr++) {
     rx_buffer[ctr]=spi_read_with_pause(buffer[ctr]);
  }
  return rx_buffer;
}     

void main(void) {
  int8 ctr;
  union {
     float val;
     int8 bte[4];
  } tx_val;
  float * fp_ptr;
  setup_spi(SPI_MASTER | SPI_MODE_0 | SPI_CLK_DIV_4);
  for (ctr=0; ctr<=100; ctr++) {
     tx_val.val=ctr*3.1415926; //silly numbers to send
     fp_ptr=spi_transfer(tx_val.bte, sizeof(tx_val.val)); //send the value and get reply
     //Now should get back 9.62E-38, followed by 0.0 to 99.0.
     //The first reply will be the four bytes 1,2,3,4, which code as 9.62E-38,
     //Then subsequent replies should be the last value sent/PI.
     printf("Value %d, is %5.2f\n\r", ctr, *fp_ptr);
     delay_ms(500); //just slow things down.....
  }
  do {
  } while (TRUE);
}


Best Wishes
carl



Joined: 06 Feb 2008
Posts: 240
Location: Chester

View user's profile Send private message

PostPosted: Tue Jul 24, 2012 5:24 am     Reply with quote

Ttelmah thank you very much.

There is a lot to understand here and absorb.
I will try it out and can learn at lot from it.

Thankyou
Carl
Display posts from previous:   
Post new topic   Reply to topic    CCS Forum Index -> General CCS C Discussion All times are GMT - 6 Hours
Goto page 1, 2  Next
Page 1 of 2

 
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