|
|
View previous topic :: View next topic |
Author |
Message |
carl
Joined: 06 Feb 2008 Posts: 240 Location: Chester
|
SPI Help |
Posted: Mon Jul 23, 2012 3:53 am |
|
|
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
|
|
Posted: Mon Jul 23, 2012 4:48 am |
|
|
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
|
|
Posted: Mon Jul 23, 2012 4:57 am |
|
|
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
|
|
Posted: Mon Jul 23, 2012 5:19 am |
|
|
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
|
|
Posted: Mon Jul 23, 2012 5:46 am |
|
|
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
|
|
Posted: Mon Jul 23, 2012 6:34 am |
|
|
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: 9229 Location: Greensville,Ontario
|
|
Posted: Mon Jul 23, 2012 7:12 am |
|
|
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
|
|
Posted: Mon Jul 23, 2012 7:17 am |
|
|
good point
I will, thanks |
|
|
ckielstra
Joined: 18 Mar 2004 Posts: 3680 Location: The Netherlands
|
|
Posted: Mon Jul 23, 2012 7:46 am |
|
|
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
|
|
Posted: Mon Jul 23, 2012 7:54 am |
|
|
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: 9229 Location: Greensville,Ontario
|
|
Posted: Mon Jul 23, 2012 1:58 pm |
|
|
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
|
|
Posted: Tue Jul 24, 2012 2:23 am |
|
|
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
|
|
Posted: Tue Jul 24, 2012 3:13 am |
|
|
Hmmm....
Changed the mode from '0' to '1' and it seems to be working correctly.
BUT WHY? |
|
|
Ttelmah
Joined: 11 Mar 2010 Posts: 19520
|
|
Posted: Tue Jul 24, 2012 5:12 am |
|
|
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
|
|
Posted: Tue Jul 24, 2012 5:24 am |
|
|
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 |
|
|
|
|
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
|