Loss of data between two MC9S08GT16A via SPI

cancel
Showing results for 
Show  only  | Search instead for 
Did you mean: 

Loss of data between two MC9S08GT16A via SPI

3,084 Views
JOTAEME
Contributor I
Hi, I'm trying to communicate two HCS08GT16A through the SPI protocol.
This is the intended behavior of the system:
-Master sends a byte to slave and turns on a LED. It waits for confirmation from slave to send second byte.
-Slave turns on a LED when it receives THE byte Master sent. It then sends a byte of ACKnowledge to Master and waits for a second byte.
-Master then sends second byte, turns on LED and waits for second ACK from Slave.
-Slave receives second byte, turns on LED, sends ACK and waits for third byte.
-The process goes on forever...

I've accomplished bidirectional communication and the codes for both Master and Slave work just fine. The problem I have is that when it sends the 16th byte, the whole thing freezes for a while and then it starts again. I've tried different ways but always happens on the 16th byte.

Does anybody know the reason of this?
Labels (1)
0 Kudos
Reply
8 Replies

995 Views
peg
Senior Contributor IV
Hi Jotaeme,
 
It would seem to me that the problem is unlikely to be with the SPI or its setup, as once one transaction is done, the rest is just the same thing over and over again. More likely it has to do with how you are scheduling this.
How does the master know that the slave is ready to acknowledge (i.e. loaded the ack byte in the SPID) before it clocks it in?
The pertinent bits of your code would help here rather than stabbing around in the dark.
 
0 Kudos
Reply

995 Views
JOTAEME
Contributor I
OK, I'll try to explain better.

This is the code for the master:
In main.cpp:
    void main(void) {
      MCU_init(); /* call Device Initialization */
      asm SEI;
      Delay();
          while (!SPI1S_SPTEF); // wait until transmit buffer is empty
          PTED_PTED2 = 0; //Slave Select set in low
      SPI1D = 0x12; // Transmit byte
          PTDD_PTDD1 = 1; // Turn on LED to indicate a byte has been sent
          Delay();
          PTDD_PTDD1 = 0; //Turn off LED
          Delay();
          asm CLI;
          for(;:smileywink: {
    }
From here, the master will wait for a response from the slave to send the second byte, and so on. This will happen in the ISR for the spi in the MCUinit.c function of the master:
__interrupt void isrVspi1(void)
{
      asm SEI;
      SPI1S; // Acknowledge flag
      dato = SPI1D; // Acknowledge flag
      if(dato==0xB3){ //0xB3 is the ACK byte sent by the slave when it receives a valid byte from the master
        switch(val){ // val is initialized as 'two' to send the second byte when it enters this routine for the first time, since the first one was already sent from the main() function
          case one:  {
            PTDD_PTDD3 = 1; // Turn on LED to indicate a byte has been received
            Delay();
            PTDD_PTDD3 = 0;
            Delay();
            while (!SPI1S_SPTEF); // wait until transmit buffer is empty
            PTED_PTED2 = 0; //Slave Select set in low
            SPI1D = 0x12; // Transmit byte
            PTDD_PTDD1 = 1; // Turn on LED to indicate a byte has been sent
            Delay();
            PTDD_PTDD1 = 0;
            Delay();
            val = two; // Change the value of 'val' so it'll send the next byte on the next time
            break;
          }
          case two: {
            PTDD_PTDD3 = 1;
            Delay();
            PTDD_PTDD3 = 0;
            Delay();
            while (!SPI1S_SPTEF); // wait until transmit buffer is empty
            PTED_PTED2 = 0; //Slave Select set in low
            SPI1D = 0x83; // Transmit byte
            PTDD_PTDD1 = 1;
            Delay();
            PTDD_PTDD1 = 0;
            Delay();
            val = three;
            break;  
          }
          case three: {
            PTDD_PTDD3 = 1;
            Delay();
            PTDD_PTDD3 = 0;
            Delay();
            while (!SPI1S_SPTEF); // wait until transmit buffer is empty
            PTED_PTED2 = 0; //Slave Select set in low
            SPI1D = 0x4A; // Transmit byte
            PTDD_PTDD1 = 1;
            Delay();
            PTDD_PTDD1 = 0;
            Delay();
            val = four;
            break;
          }
          case four: {
            PTDD_PTDD3 = 1;
            Delay();
            PTDD_PTDD3 = 0;
            Delay();
            while (!SPI1S_SPTEF); // wait until transmit buffer is empty
            PTED_PTED2 = 0; //Slave Select set in low
            SPI1D = 0x09; // Transmit byte
            PTDD_PTDD1 = 1;
            Delay();
            PTDD_PTDD1 = 0;
            Delay();
            val = one;
            break;  
          }
        }
      }
   asm CLI;
 }
This routine sends 4 different bytes consecutively to the receiver, one for each ACK received from the slave.

The code for the slave is as follows:
In the main.cpp it just waits and does nothing, so: for(;:smileywink:.
In the MCUinit.c:
    __interrupt void isrVspi1(void)
      {
       asm SEI;
       SPI1S; /*Acknowledge flag*/
       glob = SPI1D; /*Acknowledge flag*/
       switch(glob){
          case 0x12:   {
          PTAD_PTAD4 = 0;
          PTAD_PTAD5 = 0;
          PTAD_PTAD6 = 0;
          PTAD_PTAD7 = 0; // Turn off all LEDs
          PTAD_PTAD7 = 1; // Turn on the LED that corresponds to the byte received
          while (!SPI1S_SPTEF); // wait until transmit buffer is empty
          SPI1D = 0xB3; // Transmit byte
          Delay();
          break;  
        }
        case 0x83: {
          PTAD_PTAD4 = 0;
          PTAD_PTAD5 = 0;
          PTAD_PTAD6 = 0;
          PTAD_PTAD7 = 0;
          PTAD_PTAD6 = 1;
          while (!SPI1S_SPTEF); // wait until transmit buffer is empty
          SPI1D = 0xB3; // Transmit byte
          Delay();
          break;
        }
        case 0x4A:{
          PTAD_PTAD4 = 0;
          PTAD_PTAD5 = 0;
          PTAD_PTAD6 = 0;
          PTAD_PTAD7 = 0;
          PTAD_PTAD5 = 1;
          while (!SPI1S_SPTEF); // wait until transmit buffer is empty
          SPI1D = 0xB3; // Transmit byte
          Delay();
          break;  
        }
        case 0x09: {
          PTAD_PTAD4 = 0;
          PTAD_PTAD5 = 0;
          PTAD_PTAD6 = 0;
          PTAD_PTAD7 = 0;
          PTAD_PTAD4 = 1;
          while (!SPI1S_SPTEF); // wait until transmit buffer is empty
          SPI1D = 0xB3; // Transmit byte
          Delay();
          break;
        }
        default: {
          PTAD_PTAD4 = 1;
          PTAD_PTAD5 = 1;
          PTAD_PTAD6 = 1;
          PTAD_PTAD7 = 1; // If the byte is different from the four the master is supposed to send, turn on   all LEDs to indicate an error in receiving a byte from the master
          while (!SPI1S_SPTEF); // wait until transmit buffer is empty
          SPI1D = 0xB3; // Transmit byte
          Delay();
          break;    
        }
      }
      asm CLI;
}
Here, the slave will wait for a byte from the master, and when it receives it, it'll turn on a LED to show which byte was received. When it does this, it sends back the ACK byte so that the master sends the next byte and the cycle can continue.

Also, the code for the Delay() function is as follows:
void Delay(void){
  unsigned int b;
  unsigned char a;
  for(a=0;a<0x02;a++)
  {
      for(b=0;b<0xFFFF;b++)
      {
        ;
      }
  }
}
I've tried to change the values of this function to modify the time of delay, but it has no effect in the freezing.

I hope it's as clear to you as it is to me. I have no idea where it could be failing; it seems pretty easy to me, no big deal. But still is not working properly. I've made some modifications to this code, changing the number of LEDs (that is, the number of bytes) that are handled, but it ALWAYS happens the same: the master transmits 16 correct data bytes (because the LEDs on the slave turn on in the correct order) and the slave transmits 16 correct ACK bytes (because the receiver LED on the master turns on after the slave's LEDs are turned on). Then, nothing happens. The receiver LED doesn't turn on the 17th time, so it could be one of these events that happen:
-The slave received correctly the 16th byte but failed to send the 16th ACK byte.
-the slave sent correctly the 16th ACK byte but the master failed to receive it.
Since the master's receiver LED never turned on, it remains waiting so it won't send a next byte to the slave. As a consecuence of this, the receiver will also remain waiting; so nothing happens on either side. The odd thing is that after a while (say maybe a minute or two) the system starts back again as if nothing happened. Weird, huh?
Oh, by the way, I've checked the watchdog and it's disabled; so it's not that...

Pleeeaaase someone help me ASAP; I'm in trouble if I can't solve this.

Thanx
0 Kudos
Reply

995 Views
bigmac
Specialist III
Hello,
 
It would seem that the possible reason for loss of synchronisation is within the slave ISR.  The delay() function is called each time, and this will potentially cause the slave to get out of step with the master.  The delay is not required - you should exit the ISR as quickly as possible.  The return byte will sit in the SPI send buffer until the next transfer initiated by the master.  At this point the value will be returned to the master.
 
Another potential problem with the master, depending on the SPI mode you use - you appear to make the SS output active low, but do not return SS inactive.  This can be done at the beginning of the master ISR, when the SPI transfer is complete.
 
There also seems to be some misconception about SPI operation.  The byte returned from the slave in response to the command byte is not the acknowledge byte for that command.  If you need to recover the acknowledge byte prior to sending the next command byte, you will need to separately interrogate the acknowledge value prior to the command.  The sequence of events for the master might be as follows -
 
Send first command - garbage returned at completion of send
Send dummy value - first acknowledge value returned
Send second command - ignore return (migh be first ack again)
Send dummy value - second acknowlege value returned
Etc.
 
When the slave receives the "dummy value", it might either do nothing, or perhaps place a null in the send buffer.
 
A further general comment that it is usually inappropriate to include a lengthy delay within any ISR.  While it may not matter for your present simple (master) example, the delay would prevent any other interrupts from being serviced without the significant additional complication of allowing for nested interrupts.  Something to be avoided if possible.
 
Regards,
Mac
 
0 Kudos
Reply

995 Views
JOTAEME
Contributor I
Hi.
I've managed to solve this problem by getting out of the ISR as quick as possible and doing all the byte verification and SPI writing in the main, and also enabling interrupts during this whole process.
Now I have another problem...
When the master sends its first byte, the slave takes it as garbage (turns on all LEDs indicating it's neither of the bytes its supposed to receive). From there on, it works perfectly.
I tried to make sure the slaves' buffer didn't have garbage at first, so on the MCUinit I wrote the line
SPI1D = 0x00;
It worked fine in the sense that the slave turned on the first LED. But now, on the SECOND byte, it receives it as garbage. This means that the problem is indeed related to the slaves' buffer, right? The thing is that now I tried to do the same I did in the MCUinit, but it doesn't work. I've placed it on the ISR after the data is written in a variable, on the main before and/or after anything happens, after each ACK sending...anyway, I can't think of any other spot to put this instruction. Does anybody think the problem could be related to anything else I'm missing?
Thanx.
0 Kudos
Reply

995 Views
bigmac
Specialist III
Hello,
 
I am somewhat confused by the description of your current problem.  To clarify the problem you might list the following information on a byte-by-byte basis -
 
Byte value sent by the master.
Byte value returned by slave (preloaded into the slave SPI buffer prior to its receipt of master byte).
Your expected return value, if different from actual return value.
Next byte value sent by master, etc.
 
Perhaps I should have mentioned in my previous post that the use of interrupts for the SPI master code is usually unnecessary unless the SPI clock rate is quite low.  Since you are communicating with another MCU as the slave, there is no reason to use a slow clock.  The use of polling should simplify your master code.
 
A function to transfer a single byte in both directions would have the following sequence -
  1. Wait until SPTEF flag is set (send buffer is ready)
  2. Write send byte to SPI1D register
  3. Wait until SPIF flag is set (transfer is complete)
  4. Read SPI1D register for byte value returned by slave (and to clear flag)
This comment applies to the master only - the slave should continue to use interrupts.
 
Regards,
Mac
 
0 Kudos
Reply

995 Views
tonyp
Senior Contributor II
It seems my guess was not far from the truth.  Your code loses sync.  I attach my sloppy attempt to reduce your code (to be able to understand it better), and then try to correct it by adding a missing ELSE part.

(Since I don't normally use C I haven't checked your code or my changes with a compiler. Consider it pseudocode.)

Every time you do NOT receive an ACK from the slave (the missing ELSE part of your IF clause inside the ISR), you do not cause a new retransmit from the slave.  This is, hopefully, corrected simply by adding the dummy PutChar(0x00).

But I feel your code needs a major re-organization because even if you can get it to work, it is poorly structured.

0 Kudos
Reply

995 Views
tonyp
Senior Contributor II
Without any clues about your actual code, it's hard to say what may be wrong in a world of infinite possibilities.  For example, I commonly use 16-byte buffers for interrupt-driven SPI/SCI, etc.   So, the number 16 would have me checking the full queue condition handling for problems.  Or, if using fixed delays between each cycle, how long the LEDs stay on at either side could cause the system to accumulate a small synchronization error (due to minor bus speed differences) that shows up every 16 bytes as lost communication for one (or more) send/ACK cycle until it resynchronizes spontaneously.
 
Have you tried stepping with the debugger between the 15th and 17th bytes?


Message Edited by tonyp on 2007-11-10 09:20 AM
0 Kudos
Reply

995 Views
bigmac
Specialist III
Hello,
 
Is it possible the one (or both) of the MCU have COP timeout occurring?  Your code will need to periodically clear the COP timer, or the timer should be disabled.
 
Regards,
Mac
 
0 Kudos
Reply