|
|
View previous topic :: View next topic |
Author |
Message |
Holger Guest
|
SPI connection between PIC18F6723 and PIC18F4523 |
Posted: Sat Oct 25, 2008 9:17 am |
|
|
Hello,
I'm using CCS 4.068 and trying to connect two PIC via SPI. I defined the PIC18F6723 as Master using SPI2 (SPI1 is not connected to the second PIC). The PIC18F4523 is configured as Slave and there are no more slaves on the bus.
After starting first the Master and then the Slave (first the slave and then the master results in a bit shift of the byte the slave gets), the slave is receiving the bytes from the master in a correct way.
But now my problem is, that I am sending this byte added by 2 (for testing) back to the master, but the master receives the same byte as he had send to the slave. It seams as if the master is reading its own byte. By sending a "a" to the slave the slave receives the "a" he now should send a "c" but the master receives a "a".
What am i doing wrong?
Code Master:
Code: |
#include <18F6723.h>
#FUSES NOLVP //No low voltage prgming, B3(PIC16) or B5(PIC18) used for I/O
#FUSES HS //High speed Osc (> 4mhz for PCM/PCH) (>10mhz for PCD)
#FUSES MCLR //Master Clear pin enabled
#FUSES NOWDT //No Watch Dog Timer
#FUSES PUT //Power Up Timer
#define crystal 10000000
#use delay(clock = crystal)
#use rs232(baud=9600, parity=N, xmit=PIN_G1, rcv=PIN_G2, bits=8, stream=DEBUG)
#define PIC2_DI PIN_D4
#define PIC2_DO PIN_D5
#define PIC2_CLK PIN_D6
#define StatusLED PIN_F2
void main()
{
char c,b;
setup_spi2(SPI_MASTER|SPI_H_TO_L|SPI_CLK_DIV_16);
output_bit(StatusLED,0);
delay_ms(1000);
output_bit(StatusLED,1);
output_low(PIC2_DI);
output_low(PIC2_CLK);
while(1) {
if(kbhit()) { // Char available from PC ?
c = getc(); // If so, get it
spi_write2(c);
while(!spi_data_is_in2());
b=spi_read2(0);
putc(b);
}
}
}
|
Code Slave:
Code: |
#include <18F4523.h>
#FUSES NOWDT //No Watch Dog Timer
#FUSES HS //High speed Osc (> 4mhz for PCM/PCH) (>10mhz for PCD)
#FUSES PUT //Power Up Timer
#FUSES NOLVP //No low voltage prgming, B3(PIC16) or B5(PIC18) used for I/O
#FUSES NOPBADEN //PORTB pins are configured as digital I/O on RESET
#FUSES MCLR //Master Clear pin enabled
#FUSES NOXINST //Extended set extension and Indexed Addressing mode disabled (Legacy mode)
#use delay(clock=10000000)
#define StatusLED PIN_C0
#use rs232(baud=9600, parity=N, xmit=PIN_C6, rcv=PIN_C7, bits=8, stream=DEBUG)
void main(void)
{
int8 c, b =65;
int8 i;
setup_spi(SPI_SLAVE | SPI_H_TO_L |SPI_SS_DISABLED);
output_bit( StatusLED, 0);
delay_ms(1000);
output_bit( StatusLED,1);
while(true){
while(!spi_data_is_in());
c = spi_read();
spi_write(c+2);
putc(c);
}
}
|
|
|
|
Guest
|
|
Posted: Sat Oct 25, 2008 9:33 am |
|
|
Timing.
When the spi port sends a byte, one is received back _at the same time. Hence your test for data received, will immediately return 'true'. Then the Master clocks out another dummy byte, and reads the return, but with no time allowed for the slave to have actually loaded the return.
The master is in control of the bus, and bytes cannot be received, without it clocking them. Clock out the first byte, then wait for this to be sent (just use the SPI_read function). Then _wait_ for the slave to load the reply, and clock this back. So:
Code: |
//Master
while(1) {
if(kbhit()) { // Char available from PC ?
c = getc(); // If so, get it
b=spi_read2(c);
//Using the read, ensures the byte has actually sent
delay_us(10);
b=spi_read2(0);
putc(b);
}
}
|
Make the wake up delay on the slave, shorter than on the master.
Don't fiddle with the SPI pins yourself. Let the hardware handle them.
In the slave, you will need to throw away the dummy character, before looping to wait for the next incoming one.
Best Wishes |
|
|
Holger Guest
|
|
Posted: Sat Oct 25, 2008 9:52 am |
|
|
I have already tried to send the byte first, then wait 50ms and read (and resending it) again. But unfortunately it did not work.
Code: |
c = getc();
b=spi_write2(c);
delay_ms(50);
b=spi_read2(c);
putc(b);
|
|
|
|
Ttelmah Guest
|
|
Posted: Sun Oct 26, 2008 4:30 am |
|
|
OK.
Reading it's 'own byte', is not that abnormal. The register used is the same both ways. That it is happening even with a delay, suggests possibly that the CCS code is doing something 'screwy', and not actually performing the second write...
On your 'wake up' bit shift, this is because of the polarity selected for the data. the slave sees the edge when the master enables the bus, and clocks a spurious bit in at this point.
Have the slave 'poll' the SCLK input, and check the master is driving it, _before_ it enables the SPI hardware.
Historically, I don't 'like' the CCS SPI functions. For my money, they are incorrectly written, for handling the SPI hardware, especially in interrupt driven applications. In such an application, on the slave, you want to just read the SPI data register in the handler ASAP (he CCS code adds lots of testing), and in the master, you want to load the register (again without testing), and do other things till the hardware signals it has completed the job. The SPI_DATA_IS_IN function has improved things a lot.
Therefore, I always use my own functions. So, let's try proving if the problem is hardware or software, and use these instead:
You still need to use the CCS initialisation, and I'd test the SCLK line on the slave, and wait for this to go high, before enabling the hardware here.
Code: |
#byte SSPBUF = 0xFC9
#byte SSPSTAT = 0xFC7
#byte SSPBUF2 = 0xF66
#byte SSPSTAT = 0xF64
/* Now the SSP handler code. Using my own, since the supplied routines test the wrong way round for my needs */
#DEFINE READ_SSP() (SSPBUF)
#DEFINE SSP_HAS_DATA ((SSPSTAT &1)!=0)
#DEFINE WAIT_FOR_SSP() while((SSPSTAT & 1)==0)
#DEFINE WRITE_SSP(x) SSPBUF=(x)
#DEFINE READ_SSP2() (SSPBUF2)
#DEFINE SSP2_HAS_DATA ((SSPSTAT2 &1)!=0)
#DEFINE WAIT_FOR_SSP2() while((SSPSTAT2 & 1)==0)
#DEFINE WRITE_SSP2(x) SSPBUF2=(x)
//Master
int8 dummy;
c=getc();
if (SSP2_HAS_DATA) dummy=READ_SSP2(); //Ensure SSP is empty
WRITE_SSP2(c);
WAIT_FOR_SSP2(); //Ensure transaction has completed
dummy=READ_SSP2(); //Empty buffer
delay_us(20); //Give slave time to load reply
WRITE_SSP2(0);
WAIT_FOR_SSP2(); //Ensure transaction has completed
c=READ_SSP2(); //Get reply
//Slave
while(true){
WAIT_FOR_SSP();
c = READ_SSP();
WRITE_SSP(c+2);
putc(c);
WAIT_FOR_SSP(); //Wait for the master to receive the byte sent
c=READ_SSP(); //Clear the buffer here, so I can wait for the next byte
}
|
Now several things here are not necessary (you don't need to empty the buffer in the master etc.), but it should be 'dead safe', and prove whether the hardware works.
Best Wishes |
|
|
Holger Guest
|
|
Posted: Sun Oct 26, 2008 5:27 pm |
|
|
Thank you for the help.
Just for others reading the code from Ttelmah there is a small writing mistake in the code:
#byte SSPSTAT = 0xF64
should be
#byte SSPSTAT2 = 0xF64
At the moment I can send characters to each PIC but they are corrupted (every time I start the board) . How can I synchronize the two pic's so they do not have a byte shift or other effects? |
|
|
RLScott
Joined: 10 Jul 2007 Posts: 465
|
|
Posted: Sun Oct 26, 2008 6:34 pm |
|
|
Holger wrote: | Thank you for the help.
...At the moment I can send characters to each PIC but they are corrupted (every time I start the board) . How can I synchronize the two pic's so they do not have a byte shift or other effects? |
Whenever you use SS disabled on the slave, it is always possible to get out of sync and stay out of sync. This would happen, for example, if the master started sending a byte before the slave was powered up and out of reset. The slave might see a portion of a byte. One solution is to implement SS. Use a general purpose output from the master and wire it into the SS input on the slave. Whenever you lower SS, the bit counter on the slave is reset. If you can't use SS on the slave, then you might consider forcing a reset of the bit counter on the slave at a time in the protocol when you are fairly sure the master is not transmitting. Timing a short time-out after the reception of a byte could work. Make sure the time-out is long enough to cover one full byte. To reset the bit counter, disable the SPI port and re-enable it. You don't need to call any CCS functions. Just clear SSPCON.SSPEN and then set it again. _________________ Robert Scott
Real-Time Specialties
Embedded Systems Consulting |
|
|
Ttelmah Guest
|
|
Posted: Mon Oct 27, 2008 3:12 am |
|
|
Yes.
The other 'route', not using an extra line, is to ensure that at some point in your 'comms', there is always a period where nothing happens. Have you comm 'packet', have an identifiable 'header' pattern (perhaps something like 0xAA 0x55). Have a 'resync' function, in the slave, which turns off the SPI hardware, then waits for the 'idle' period, and re-enables it. Call this at the start to get the system 'in sync', and call it again, if you do not see the 'header' pattern where you expect it.
Using an extra line is really easier and quicker.
Sorry about the 'typo'....
Best Wishes |
|
|
Holger Guest
|
|
Posted: Mon Oct 27, 2008 9:50 am |
|
|
Hello,
Thank you all, unfortunately I have and had the SS wire connected. But as I am just testing only 1 Slave at the SPI line, I thought I would start the test program as simple as possible and decided not to use the SS as I do not have to select between slaves.
I had not suspected that this simplification made it more complicated :-)
Now the two PIC's communicate without any problems, Thanks.
@Ttelmah
I have seen your Macros for the SPI communication in other threads to. But there always was 1 more definition #define CLEAR_WCOL() SSPCON = SSPCON & 0x3F. What is this for? |
|
|
Ttelmah Guest
|
|
Posted: Tue Oct 28, 2008 4:09 pm |
|
|
Clears the write collision bit.
Only gets set if you write directly to the buffer, without testing that a transmission has completed.
Best Wishes |
|
|
|
|
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
|