View previous topic :: View next topic |
Author |
Message |
SoBot
Joined: 20 Apr 2010 Posts: 9
|
Master/slave spi communication |
Posted: Thu Apr 22, 2010 4:42 am |
|
|
I am communicating over SPI between two microcontrollers. Each one is on its own pcb and the spi port is connected with a ribbon cable (pcb grounds also connected). For some reason the data does not transfer correctly. I send the same byte out every 100ms. No matter what I send, a 0xff is received on the other end. What can be wrong? Is it a software problem or can it be in the hardware problem?
Transmitter code:
Code: |
setup_spi(SPI_MASTER | SPI_H_TO_L | SPI_CLK_DIV_4 );
While(1)
{
spi_write(0x01);
delay_ms(100);
}
|
Receiver code:
Code: |
setup_spi(SPI_SLAVE | SPI_H_TO_L | spi_ss_disabled );
While(1)
{
while(!spi_data_is_in());
data = spi_read();
if (data == 0x01)
output_low(PIN_C0);
}
|
|
|
|
PCM programmer
Joined: 06 Sep 2003 Posts: 21708
|
|
Posted: Thu Apr 22, 2010 1:32 pm |
|
|
Post your PIC, your compiler version, and a list of the connections
between the two PICs. |
|
|
SoBot
Joined: 20 Apr 2010 Posts: 9
|
|
Posted: Fri Apr 23, 2010 6:40 am |
|
|
I got the problem. When I looked at my connections to list them, I saw that I had wrongly wired it. My bad
Thanks |
|
|
avami
Joined: 10 Jun 2010 Posts: 11 Location: Mashhad
|
SPI |
Posted: Thu Jun 10, 2010 6:27 am |
|
|
Hi
I have a problem with SPI like this.
It doesn't work well, it sometimes send data and the others no!
Transmitter code:
Code: |
#include <18f452.h>
#use delay(clock=12000000)
#FUSES HS
void main()
{
set_tris_a(0x0f);
setup_spi(SPI_MASTER|SPI_L_TO_H|SPI_CLK_DIV_4);
delay_ms( 1000 );
while(1)
{
SPI_WRITE(80);
output_high(PIN_a1);
delay_ms( 1000 );
output_low(PIN_a1);
delay_ms( 500 );
}
}
|
Receiver code:
Code: |
#include <18f452.h>
#use delay(clock=12000000)
#FUSES HS
int value;
#int_SSP
SSP_isr()
{
clear_interrupt(int_ssp);
disable_interrupts(GLOBAL);
value=spi_read(0);
if (value==80)
{
output_high(PIN_a0);
delay_ms(500);
output_low(PIN_a0);
}
enable_interrupts(GLOBAL);
}
void main()
{
set_tris_a(0x0f);
setup_spi(SPI_SLAVE|SPI_L_TO_H|SPI_CLK_DIV_4);
enable_interrupts(INT_SSP);
enable_interrupts(GLOBAL);
output_high(PIN_a1);
while(1);
}
|
|
|
|
PCM programmer
Joined: 06 Sep 2003 Posts: 21708
|
|
Posted: Thu Jun 10, 2010 11:28 am |
|
|
Quote: | Transmitter code:
void main()
{
set_tris_a(0x0f);
setup_spi(SPI_MASTER|SPI_L_TO_H|SPI_CLK_DIV_4);
delay_ms( 1000 );
while(1)
{
SPI_WRITE(80);
output_high(PIN_a1);
delay_ms( 1000 );
output_low(PIN_a1);
delay_ms( 500 );
}
}
|
You don't need to set the TRIS. You're using "standard i/o" mode, which
is the default mode of the compiler. In that mode, the compiler sets the
TRIS for you, if you use CCS functions. Because you are using
standard i/o mode, the compiler will override your TRIS settings anyway.
Also, you have set the incorrect TRIS. In a set_tris_x() statement, a '1'
bit means to set the TRIS to an input. So you are setting pins A0-A3 to
be inputs. But anyway, just delete the set_tris_a(0x0F) line. You don't
need it.
Quote: | Receiver code:
#include <18f452.h>
#use delay(clock=12000000)
#FUSES HS
int value;
#int_SSP
SSP_isr()
{
clear_interrupt(int_ssp);
disable_interrupts(GLOBAL);
value=spi_read(0);
if (value==80)
{
output_high(PIN_a0);
delay_ms(500);
output_low(PIN_a0);
}
enable_interrupts(GLOBAL);
}
void main()
{
set_tris_a(0x0f);
setup_spi(SPI_SLAVE|SPI_L_TO_H|SPI_CLK_DIV_4);
enable_interrupts(INT_SSP);
enable_interrupts(GLOBAL);
output_high(PIN_a1);
while(1);
}
|
Don't enable/disable Global interrupts inside a interrupt routine.
This is all handled for you automatically by the PIC hardware and the
compiler's interrupt code. The PIC hardware disables global interrupts
when it gets an interrupt. When the program exits from the interrupt
routine, it executes a RETFIE instruction that re-enables Global interrupts
at the time. You don't need to do it. And, you should not do it. If you
enable Global interrupts inside an interrupt routine, the program can
crash, because nested interrupts are not supported. Delete the two
lines that disable/enable global interrupts inside the #int_ssp routine.
Also, you have specified a 0x00 parameter for spi_read() inside the
#int_ssp routine. This is incorrect. This parameter tells the compiler
to write 0x00 to the SSPBUF register, and is normally done to generate
the SPI clock in a Master. But the Slave does not generate a clock.
Remove the 0x00 parameter from the spi_read() statement. Don't put
in any parameter.
Again, you don't need to set the TRIS. Let the compiler do it. |
|
|
Abdulla M.A.
Joined: 28 Mar 2010 Posts: 30 Location: Baghdad, Iraq
|
|
Posted: Thu Jun 10, 2010 12:00 pm |
|
|
Just add a spi_read statement in the master program, the program will work fine.
just add it and see.
Abdulla _________________ "A scientist can discover a new star, but he cannot make one. He would have to ask an engineer to do that."
"For an optimist the glass is half full, for a pessimist it's half empty, and for an engineer is twice bigger than necessary." |
|
|
PCM programmer
Joined: 06 Sep 2003 Posts: 21708
|
|
Posted: Thu Jun 10, 2010 12:31 pm |
|
|
Quote: | Just add a spi_read statement in the master program, the program will work fine. |
That's not true. He needs to make the changes that I said in my post.
Also, he needs to make one more change to the Receiver code to
disable the Slave Select pin (since he is not using it in the Master code).
Change the Receiver setup_spi() statement to this:
Quote: | setup_spi(SPI_SLAVE|SPI_L_TO_H| SPI_SS_DISABLED); |
Notice that the clock speed divisor value is removed, and the Slave
select disable parameter is added.
With these changes, you will get the blinking LED about once per second
on the receiver, which shows it is working. |
|
|
asmallri
Joined: 12 Aug 2004 Posts: 1635 Location: Perth, Australia
|
|
Posted: Thu Jun 10, 2010 5:55 pm |
|
|
PCM programmer wrote: |
Change the Receiver setup_spi() statement to this:
Quote: | setup_spi(SPI_SLAVE|SPI_L_TO_H| SPI_SS_DISABLED); |
Notice that the clock speed divisor value is removed, and the Slave
select disable parameter is added. |
This is not very intuitive. I would have interpreted the SPI_SS_DISABLED to mean the chip select on the SPI slave is disabled. In such a scenario if the master and slave get out of sync, then they will not be able to get back in sync. The CS on an SPI addresses this problem. _________________ Regards, Andrew
http://www.brushelectronics.com/software
Home of Ethernet, SD card and Encrypted Serial Bootloaders for PICs!! |
|
|
PCM programmer
Joined: 06 Sep 2003 Posts: 21708
|
|
Posted: Thu Jun 10, 2010 6:14 pm |
|
|
I know that. I normally use Slave Select. He didn't have it and I was
just trying to make his code work. |
|
|
avami
Joined: 10 Jun 2010 Posts: 11 Location: Mashhad
|
|
Posted: Sat Jun 12, 2010 4:47 pm |
|
|
Thanks for your nice consideration to this matter. My circuit worked very well and the microcontroller sent the data on SPI successfully.
As you said me several points, I changed them. But I still have some questions about them (Because I'm beginner in PIC)
1. I don't need to use TRIS, so when do we use TRIS in CCS?
2. In this statement : setup_spi(SPI_SLAVE|SPI_L_TO_H| SPI_SS_DISABLED);
what is "SPI_SS_DISABLED"? I want to use this SPI for communication between two RF Module by PIC and is it a problem for chip select?
3. In spi_read() statement, you said me to remove 0x00 and it is normally done to generate the SPI clock in Master. What does it do exactly?
4. About Global interrupt, I removed them and it worked successfully as you said at first, and when I added disable/enable global interrupts inside the #int_ssp routine in next stage it still worked well !? |
|
|
Ttelmah
Joined: 11 Mar 2010 Posts: 19520
|
|
Posted: Sun Jun 13, 2010 2:48 am |
|
|
In CCS, there are three different 'ways' of using the ports.
The first is 'Standard_io' mode. This is the 'standard' (in the name....). With this the compiler handles TRIS for you. For 99% of applications, this is fine.
Downsides of 'standard_io', are in two areas:
If you want to read or write in one operation, an whole 'byte' wide port, but leave a few pins set to use the opposite I/O direction. So (for instance), if you read the whole of portB, inside an interrupt handler, when using the portB interrupt on change, but want to leave (say) B0, as an output.
If you want the ultimate in speed. In standard I/O mode, the compiler adds two instructions to each I/O operation, to control the TRIS for that operation. This costs two machine cycles.
These are then when the other I/O modes are needed. Fixed_io, and fast_io.
With fixed_io, the behaviour is basically the same (the instructions to control the I/O direction are still there), but _you_ can set the I/O direction to fixed values, with the fixed_io instruction. So in the interrupt handler example, you could program B0 to be an output, and this would be used, despite you 'reading' the whole port. Just what is needed for this.
With fast_io, the compiler no longer controls TRIS, the extra instructions disappear (making I/O as fast as assembler), but you then have to use TRIS.
If you need the ultimate speed, then setup the port to use fast_io mode, and you will need to set the TRIS. Otherwise, 'forget it'....
Best Wishes |
|
|
PCM programmer
Joined: 06 Sep 2003 Posts: 21708
|
|
Posted: Sun Jun 13, 2010 12:36 pm |
|
|
Quote: |
2. In this statement:
setup_spi(SPI_SLAVE|SPI_L_TO_H| SPI_SS_DISABLED);
what is "SPI_SS_DISABLED"? I want to use this SPI for communication
between two RF Module by PIC and is it a problem for chip select? |
Does the RF module have a Chip Select pin on it ?
If so, then you should use it. Look at this sample SPI driver:
http://www.ccsinfo.com/forum/viewtopic.php?t=41059&start=1
Anytime an SPI transaction occurs, it is surrounded by lines of code that
set the \CS signal low at the start, and then set it high again at the end.
That code is a PIC used as the SPI Master. See here for an SPI slave:
http://www.ccsinfo.com/forum/viewtopic.php?t=39145
Quote: |
3. In spi_read() statement, you said me to remove 0x00 and it is
normally done to generate the SPI clock in Master. What does it do exactly?
|
When the compiler sees a parameter in spi_read(), it writes it to the
SPI buffer register inside the PIC. If the SPI module is configured
as an SPI Master, then the PIC will shift out the data in the SPI Buffer
to the slave. It will also generate 8 SPI clocks.
Quote: |
4. About Global interrupt, I removed them and it worked successfully as
you said at first, and when I added disable/enable global interrupts inside
the #int_ssp routine in next stage it still worked well !?
|
Well, you might get away with it, but you're taking a huge risk, and for
no reason at all. There is no gain from it. You're just making your
program be liable to catastrophic failure. There is no support for
nested interrupts. There is support for High Priority interrupts, but
you're not using it, you don't need it, and it's not part of your program. |
|
|
avami
Joined: 10 Jun 2010 Posts: 11 Location: Mashhad
|
|
Posted: Mon Jun 14, 2010 12:20 am |
|
|
Thanks for your useful explanations again
Quote: |
Does the RF module have a Chip Select pin on it ?
|
Yes it has but I connected it to GND for ever, because I use just one device.
I have another question about SPI. I want to use SPI with external Interrupt.
But it didn't work.
Transmitter code:
Code: |
#include <18f452.h>
#use delay(clock=12000000)
#FUSES HS
void main()
{
setup_spi(SPI_MASTER|SPI_L_TO_H|SPI_CLK_DIV_4);
delay_ms( 1000 );
while(1)
{
SPI_WRITE(80);
output_high(PIN_a1);
delay_ms( 100 );
output_low(PIN_a1);
delay_ms( 100 );
}
}
|
Receiver code:
Code: |
#include <18f452.h>
#use delay(clock=12000000)
#FUSES HS
#int_EXT1
int value;
void EXT1_isr(void)
{
value=spi_read();
if (value==80)
{
output_high(PIN_a0);
delay_ms(500);
output_low(PIN_a0);
}
}
void main()
{
delay_ms(1000);
setup_spi(SPI_SLAVE|SPI_L_TO_H|SPI_SS_DISABLED);
ext_int_edge(H_TO_L);
enable_interrupts(INT_EXT1);
enable_interrupts(GLOBAL);
output_high(PIN_a1);
while(1);
}
|
|
|
|
PCM programmer
Joined: 06 Sep 2003 Posts: 21708
|
|
Posted: Tue Jun 15, 2010 3:10 pm |
|
|
I don't know why you are doing this. The spi_read() line in the #int_ext
routine will just load the contents of SSPBUF into 'value'. If SSPBUF
contains garbage, then 'value' will loaded with garbage. You are not
checking to see if valid data exists in the SSPBUF register before you
read it. This can be done with the spi_data_is_in() function.
The #int_ext routine is not a substitute for the #int_ssp routine.
But anyway, you have a coding problem that is shown below:
Quote: |
#int_EXT1
int value;
void EXT1_isr(void)
{
value=spi_read();
if (value==80)
{
output_high(PIN_a0);
delay_ms(500);
output_low(PIN_a0);
}
} |
Because you put the declaration of 'value' between the #int_ext1 and
the function declaration, the compiler considers this to mean there is
no interrupt routine, and it will not generate any code for it. You can
see this in the .LST file. Notice there is no ASM code generated:
Code: |
.................... #FUSES HS
....................
.................... #int_EXT1
.................... int value;
.................... void EXT1_isr(void)
.................... {
.................... value=spi_read();
....................
.................... if (value==80)
.................... {
.................... output_high(PIN_a0);
.................... delay_ms(500);
.................... output_low(PIN_a0);
.................... }
.................... }
....................
|
You can't put any lines of code in-between the #int_ext and the start of
the interrupt routine. It must be done like this:
Code: |
#int_EXT1
void EXT1_isr(void)
{ |
|
|
|
avami
Joined: 10 Jun 2010 Posts: 11 Location: Mashhad
|
|
Posted: Tue Jun 15, 2010 11:07 pm |
|
|
Quote: |
I don't know why you are doing this.
|
I want to read data from the SPI port when external interrupt is activated. I use this for connection the PIC and Rf module. When the Rf module receives data at the time a PIN will be activated and must be interrupted in order to enable it to receive data from SPI.
So how can I enable the external interrupt to receive data from SPI ? |
|
|
|