|
|
View previous topic :: View next topic |
Author |
Message |
Foppie
Joined: 16 Sep 2005 Posts: 138 Location: The Netherlands
|
SPI slave receives bits shifted to right |
Posted: Tue Dec 18, 2007 10:05 am |
|
|
Hello,
I searched this forum, but I couldn't get an answer to my question.
I use 2 PIC18F8722 connected with SPI. I have a the following code running on them:
Master: Code: | #include "18F8722.h"
#device *=16 ADC=10
#case
#FUSES NOWDT //No Watch Dog Timer
#FUSES H4 //High speed Osc (> 4mhz)
#FUSES NOPROTECT //Code not protected from reading
#FUSES NOLVP //Low Voltage Programming on B3
#FUSES NOCPD //No EE protection
#FUSES NOWRT //Program memory not write protected
#use delay(clock=40000000)
#use rs232(baud=9600, BRGH1OK, parity=n, bits=8, xmit=PIN_C6, rcv=PIN_C7, ERRORS, stream=ZIGBEE)
#use rs232(baud=9600, BRGH1OK, parity=n, bits=8, xmit=PIN_G1, rcv=PIN_G2, ERRORS, stream=GPS)
char c_spi = 0;
void spi_init()
{
setup_spi(SPI_MASTER | SPI_H_TO_L | SPI_CLK_DIV_16);
}
void spi_send(int8 data)
{
int8 result;
result = spi_read(data);
if (result > 0)
c_spi = result;
delay_ms(1);
output_high(SCK_SPI1);
delay_ms(100);
}
void main()
{
spi_init();
delay_ms(1000);
spi_send('H');
spi_send('e');
spi_send('l');
spi_send('l');
spi_send('o');
spi_send(' ');
spi_send('W');
spi_send('o');
spi_send('r');
spi_send('l');
spi_send('d');
spi_send('!');
while (1)
{
delay_ms(100);
output_toggle(PIN_H1); //toggle LED
}
} |
Slave: Code: | #include "18F8722.h"
#device *=16 ADC=10
#case
#FUSES NOWDT //No Watch Dog Timer
#FUSES H4 //High speed Osc (> 4mhz)
#FUSES NOPROTECT //Code not protected from reading
#FUSES NOLVP //Low Voltage Programming on B3
#FUSES NOCPD //No EE protection
#FUSES NOWRT //Program memory not write protected
#use delay(clock=40000000)
#use rs232(baud=9600, BRGH1OK, parity=n, bits=8, xmit=PIN_C6, rcv=PIN_C7, ERRORS, stream=USB)
#use rs232(baud=9600, BRGH1OK, parity=n, bits=8, xmit=PIN_G1, rcv=PIN_G2, ERRORS, stream=UWEAVE)
void spi_init()
{
setup_spi(SPI_SLAVE | SPI_H_TO_L | SPI_CLK_DIV_16 | SPI_SS_DISABLED);
}
void main()
{
char c_master = 0;
spi_init();
while(1)
{
c_master = spi_read(0);
if (c_master > 0)
{
fputc(c_master, USB);
c_master = 0;
}
}
} |
when I use this, I get from the USB port the string "$2Â67É+À╣62►"
when I add the following line to the slave: Code: |
while(1)
{
c_master = spi_read(0);
if (c_master > 0)
{
c_master <<= 1; // I ADDED THIS LINE
fputc(c_master, USB);
c_master = 0;
}
} | I receive: "Hdlln Vnrld " which says me there could be some timing problem or such. But whatever I do to the declaration of the SPI bus I can't seem to get it working.
Does anybody have an idea what my problem could be? |
|
|
ckielstra
Joined: 18 Mar 2004 Posts: 3680 Location: The Netherlands
|
|
Posted: Tue Dec 18, 2007 12:31 pm |
|
|
To me this looks like a synchronization problem. At program start the master and slave are not synchronized and it is very well possible that a random clock pulse is introduced. For example your spi_setup() differs from the power-on default, if the master and slave don't change the setup at exactly the same time you will have generated 1 or 2 clock pulses. Another possible cause of errors are the omission of pull-up / pull-down resistors for a defined power-on state.
The easiest way to fix this is to use a Chip Select signal which is used to tell the Slave it must be ready for communications. For added confusion Microchip has decided to call this Slave input line 'Slave Select'. Just before transmission you activate the Slave Select input and when communication is finished you disable the signal again.
If you don't have the spare pins to implement the Slave Select signal there are other synchronisation methods but these require more program memory and are slower.
A bug in your slave code: Code: | setup_spi(SPI_SLAVE | SPI_H_TO_L | SPI_CLK_DIV_16 | SPI_SS_DISABLED);
| For the slave never set a clock speed! Get rid of the SPI_CLK_DIV_16. You are lucky it is working now because the defined value for SPI_CLK_DIV_16 is 1, which is by chance equal to the defined value for SPI_SS_DISABLED.
Another luring problem is in the speed the slave can handle the incoming data. The master is sending the data at a speed of SPI_CLK_DIV_16, i.e. a single bit is sent every 16 clock cycles and a byte in 16*8=128 clock cycles.
The slave is running at the same clock speed as the master and receives a whole byte in 128 clock cycles. The processor uses 4 clock cycles to execute a single instruction, so in your while-loop there is only time to execute 128/4=32 assembly instructions. This is a bit tight....
Give the slave a bit more time by slowing down the transmission from the master, insert a delay between the bytes or use SPI_CLK_DIV_64. |
|
|
PCM programmer
Joined: 06 Sep 2003 Posts: 21708
|
|
Posted: Tue Dec 18, 2007 12:33 pm |
|
|
In your slave setup, you're specifying a clock divisor constant. Slaves
don't supply a clock. The master supplies it. You don't need that divisor.
Also in your slave code, you're giving the spi_read() function a
parameter. In CCS, that signifies that the spi module should supply a
clock. But slaves don't supply a clock.
CCS has an SPI slave example file that shows how to do the read
in the slave.
Code: |
while(!spi_data_is_in());
data = spi_read();
|
Quote: |
c:\program files\picc\examples\ex_spi_slave.c
|
|
|
|
Foppie
Joined: 16 Sep 2005 Posts: 138 Location: The Netherlands
|
|
Posted: Wed Dec 19, 2007 3:55 am |
|
|
I implemented the Slave Select signal as CKielstra said and also changed the setup to setup_spi(SPI_SLAVE | SPI_H_TO_L);
Further more I changed spi_read(0); in spi_read(); as PCMprogrammer said.
And now it is working flawless. Many thanks! |
|
|
|
|
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
|