|
|
View previous topic :: View next topic |
Author |
Message |
iw2nzm
Joined: 23 Feb 2007 Posts: 55
|
spi headache |
Posted: Fri Oct 12, 2007 6:12 am |
|
|
Hello,
I'm puzzling over a simple problem. I have two PIC18F4025 with an SPI link (SDI <-> SDO and SCK). I use spi_read / spi_write function to exchange data. The spi is working: the slave receives single bytes transmitted by the master.
Now I need to implement the following protocol:
Master <> Slave
*
byteM1 byteS1
byteM2 byteS2
byteM3 byteS3
byteM4 byteS4
In other words, the master send a '*' to synchronize the slave, then it sends its own four bytes while the slave answers with other four bytes.
This is the code for the slave:
Code: |
typedef struct {
int8 value;
int16 range[2];
} pages;
pages page[4];
#int_SSP
void SSP_isr(void) {
char rcv;
if (rcvIndex == 255) rcv = spi_read();
if ((rcv == '*' && rcvIndex == 255) || rcvIndex != 255) {
rcvIndex++;
rcvData[rcvIndex] = spi_read(page[rcvIndex]);
}
if (rcvIndex == 4) {
rcvIndex = 255;
dataReady = TRUE;
}
}
|
In the main loop I check for dataReady in order to do something.
And this is for the master:
Code: |
int8 brkProf[4];
typedef struct {
int8 status;
int8 value;
} brakes;
brakes brake[2];
.
.
.
for (;;) {
spi_write('*');
brkProf[0] = spi_read(brake[0].status);
brkProf[1] = spi_read(brake[0].value);
brkProf[2] = spi_read(brake[1].status);
brkProf[3] = spi_read(brake[1].value);
delay_ms(500);
}
|
The oscilloscope shows me some activity on the pins but the slave resets over and over....
What am I missing?
Thx,
Marco / iw2nzm |
|
|
Ttelmah Guest
|
|
Posted: Fri Oct 12, 2007 7:17 am |
|
|
Multiple things.
You only want one spi_read in the slave interrupt. The interrupt occurs, because _one_ byte is waiting. By reading two, you hold the slave inside the interrupt till the second byte arrives. However the logic then goes faulty. You cannot use spi_read(val), to write a value out the spi port on the slave. This form of the instruction, is for where a _master_ wants to clock out one byte, by reading another. On the slave, just use the spi_write instruction to put the data into the output buffer, _ready for the next master operation to transfer it_. In fact you need to always work one byte 'ahead', so what you need to do, is to load the first output byte, _when you receive the '*'_, and then advance for each character.
You are also trying to read and write a structure, when you need to access
an element... You dont show the declaration for the receive array, but assuming this is only a byte array.
So:
Code: |
#int_SSP
void SSP_isr(void) {
char rcv;
rcv = spi_read(); //The character must be read.
if ((rcv == '*' && rcvIndex == 255)
spi_write(page[0].value);
//output the first character, _ready_ for the next transfer
rcvIndex=0;
}
else {
rcvData[rcvIndex]=rcv;
rcvIndex++;
spi_write(page[rcvIndex].value);
//write one character ahead of the receive
if (rcvIndex == 4) {
rcvIndex = 255;
dataReady = TRUE;
}
}
|
Now, you also don't give the clock rate being used, but you need to pause after sending a byte, long enough for the slave to have entered the interrupt, and got the next byte ready to send back. Typically perhaps 60 or more instruction times between the writes.
Best Wishes |
|
|
iw2nzm
Joined: 23 Feb 2007 Posts: 55
|
|
Posted: Fri Oct 12, 2007 8:40 am |
|
|
Thank for your complete answer Ttelmah. I completely misunderstood the spi behavior and the manual explanation too.
Despite your suggestion the slave continues to reset itself.
Let's see what I did:
Master:
Code: |
setup_spi(SPI_MASTER | SPI_L_TO_H | SPI_CLK_DIV_16 | SPI_SS_DISABLED);
.
.
.
spi_write('*');
delay_us(50);
brkProf[0] = spi_read(brake[0].status);
delay_us(50);
brkProf[1] = spi_read(brake[0].value);
delay_us(50);
brkProf[2] = spi_read(brake[1].status);
delay_us(50);
brkProf[3] = spi_read(brake[1].value);
|
The pause between transmission is a very long time, but I want to be sure there is no problem related to this.
Slave:
Code: |
int1 dataReady = FALSE;
int8 rcvData[4];
int8 rcvIndex = 255;
setup_spi(SPI_SLAVE | SPI_L_TO_H | SPI_CLK_DIV_16 | SPI_SS_DISABLED);
.
.
.
#int_SSP
void SSP_isr(void) {
char rcv;
rcv = spi_read();
if (rcv == '*' && rcvIndex == 255) {
spi_write(page[0].value);
rcvIndex = 0;
} else {
rcvData[rcvIndex++] = rcv;
if (rcvIndex == 4) {
rcvIndex = 255;
dataReady = TRUE;
} else {
spi_write(page[rcvIndex].value);
}
}
}
|
Before I forgotten to show the rcvData declaration: as you said it's a byte array. I changed the last part of the isr because after writing the four values I still have to receive one byte. But I have no more data to transmit.
Please, might you suggest me how to debug and fix the problem?
Thanks again
Marco / iw2nzm |
|
|
ckielstra
Joined: 18 Mar 2004 Posts: 3680 Location: The Netherlands
|
|
Posted: Fri Oct 12, 2007 9:13 am |
|
|
Only something I noticed at quick glance Code: | setup_spi(SPI_MASTER | SPI_L_TO_H | SPI_CLK_DIV_16 | SPI_SS_DISABLED); | SPI_SS_DISABLED is only allowed for the slave. Check the header file for your processor and you will see the defined value conflicts with the SPI_CLK_DIV_xx definitions.
A PIC resetting itself is rare. Make sure you have:
- disabled the watchdog.
- a stable power supply.
- check for stack overflow (from top of the *.lst file).
- brown out settings that make sense (if applicable for your chip). |
|
|
iw2nzm
Joined: 23 Feb 2007 Posts: 55
|
|
Posted: Sun Oct 14, 2007 3:41 am |
|
|
UPDATE
So I solved the reset problem. Don't ask me how I did. I just cut & paste the same code in the same place and the reset gone away. I guess the CCS IDE is quite buggy, sometimes it crashes or hangs-up while I'm writing. I'm going to use an external editor such as the Programmer's notepad. Maybe it was better I didn't buy the IDE but just the compiler.
However, I partially solved the SPI issue. Now I can successfully transmit data from master to slave. It stops working (see later) when I try to send data back from slave to master.
A bit of code:
Master
Code: |
#define SPI_MODE_0_0 (SPI_L_TO_H | SPI_XMIT_L_TO_H)
#define SPI_MODE_0_1 (SPI_L_TO_H)
#define SPI_MODE_1_0 (SPI_H_TO_L)
#define SPI_MODE_1_1 (SPI_H_TO_L | SPI_XMIT_L_TO_H)
setup_spi(SPI_MASTER | SPI_MODE_1_1 | SPI_CLK_DIV_64);
for (;;) {
delay_ms(250);
spi_write('*');
delay_us(100);
brkProf[BRKSRT] = spi_read(brake[0].status);
delay_us(100);
brkProf[BRKACQ] = spi_read(brake[0].value++);
delay_us(100);
brkProf[BRKSTP] = spi_read(brake[1].status);
delay_us(100);
brkProf[BRKVAL] = spi_read(brake[1].value++);
} |
Please note all variables involved are declared as int8.
Slave:
Code: |
#define SPI_MODE_0_0 (SPI_L_TO_H | SPI_XMIT_L_TO_H)
#define SPI_MODE_0_1 (SPI_L_TO_H)
#define SPI_MODE_1_0 (SPI_H_TO_L)
#define SPI_MODE_1_1 (SPI_H_TO_L | SPI_XMIT_L_TO_H)
setup_spi(SPI_SLAVE | SPI_MODE_1_1 | SPI_SS_DISABLED);
#int_SSP
void SSP_isr(void) {
char rcv;
rcv = spi_read();
if (rcv == '*' && rcvIndex == 255) {
//spi_write(10); <--------------------- this will cause weird data
rcvIndex = 0;
} else {
rcvData[rcvIndex++] = rcv;
if (rcvIndex == 4) {
rcvIndex = 255;
dataReady = TRUE;
} //else {
//spi_write(page[rcvIndex].value); //(I try to send only 1 byte now)
//}
}
} |
If the slave send nothing all works fine. If I enable the line spi_write(10); I note strange behavior such as bit shifting in read data or loss of synchronization (byte shifting).
I'm pretty sure I mess something in the spi configuration. Since yesterday evening I'm reading a tons of post in this forum about the topic. But I'm still not able to fix the problem.
Thanks to all
Marco / iw2nzm |
|
|
Ttelmah Guest
|
|
Posted: Sun Oct 14, 2007 4:06 am |
|
|
Try just putting the byte into the SPi register.
Unfortunatly, the CCS SPI code, is not really written to handle slave transmissions. If I remember correctly, it actually waits for the transfer to complete, before returning. This is not what is wanted at all, when dealing with an interrupt driven slave. :-(
Use:
Code: |
//For PIC18 chips. Will need to change for others
#byte SSPBUF = 0xFC9
#byte SSPCON = 0xFC6
#byte SSPSTAT = 0xFC7
#bit BF = SSPSTAT.0
/* Now the SSP handler code. Using my own, since the supplied routines test the wrong way round for interrupt driven slave operations... */
#DEFINE READ_SSP() (SSPBUF)
#DEFINE WAIT_FOR_SSP() while(!BF)
#DEFINE WRITE_SSP(x) SSPBUF=(x)
#DEFINE CLEAR_WCOL() SSPCON=SSPCON & 0x3F
|
Just use the 'READ_SSP' function, to get the byte in the interrupt handler, and the 'WRITE_SSP' function, to write the returned data.
I was slightly 'hoping' that the latter versions of the compiler, might have improved this behaviour, but had to 'go DIY' in the past.
Best Wishes |
|
|
iw2nzm
Joined: 23 Feb 2007 Posts: 55
|
|
Posted: Sun Oct 14, 2007 4:28 am |
|
|
I want to say you a BIG thank you.
It works like a charm. The problem was the bad CCS code.
Furthermore, I've just killed the CCS IDE and I'm using the PN as editor.
Thanks again
Marco / iw2nzm |
|
|
|
|
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
|