SPI Master-Slave communication between two MC9S12DG256's

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

SPI Master-Slave communication between two MC9S12DG256's

Jump to solution
4,263 Views
admin
Specialist II

Hello,

I am trying to communicate between two MC9S12DG256's using SPI protocol having one as master and the other one as a slave. I wrote the following codes following some postings which were uploaded to this forum, but I couldn't make them work. These postings are

 

https://community.freescale.com/message/24342#24342

https://community.freescale.com/message/25173#25173

 

 

First the connection b/w the master and the slave:

 

// Connections:
// MASTER                     SLAVE
// -------------------------------------
// PS4 (MISO) <------------   PS4(MISO)
// PS5 (MOSI) ------------>   PS5(MOSI)
// PS6 (SCLK) ------------>  PS6(SCLK)
// PS7 (SS)   <-- Vcc   
// PS0 (GPIO) ------------>   PS7(SS)
// GND             -------------   GND

 

 

Then, the code for the master:

 

void main(void) {
  volatile byte data; 
 
  PLL_init();  // set system clock frequency to 24 MHz

  // SPI communication initialization as a master
  SPI0BR  = 0b00000011;    // Baud rate = 24MHz/16 = 1.5MHz 0x53; 
  SPI0CR2 = 0b00000010;  // SPI0CR2_SPISWAI=1 : Stop SPI clocks when in wait mode
  SPI0CR1 = 0b01010110;  // Enable SPI ; Master mode ; CPHA = 1 ;

                                                 // SSOE=1 (SS input with MODF feature)      
  DDRS |= 0x61;   // 0b 0 1 1 0 0 0 0 1 : PS7(SS) and PS4(MISO) are input and PS6(SCK)

                               // and PS5(MOSI) are output, PS0 is output for setting and clearing slave's SS
 
  PTS &= (~0x01);                  // Low slave's SS


  for( ; ; ){   
   
    //PTS &= (~0x01);              // Low slave's SS
               
    // Send a byte to the slave
    while( SPI0SR_SPTEF == 0); // Wait until SPIDR becomes empty.
    SPI0DR = 0x04;                         //  Clear the flag SPI0SR_STEF 

    while( SPI0SR_SPIF == 0 );   // Wait until the end of an SPI transfer.   
    data = SPI0DR;                        // Read or write to clear SPI0SR_SPIF flag       
   
    // Receive a byte from the slave
    while(SPI0SR_SPTEF == 0);  // Wait until the SPI0DR is empty
    SPI0DR = 0x00;                         // Read SPI0DR to clear the SPI0SR_SPTEF flag
    while(SPI0SR_SPIF == 0);      // Wait until a SPI transfer is finished   
    data = SPI0DR;                         // Write the received data to data           
   
    //PTS |= (0x01);               // High slave's SS           
  }
}

 

  

For the slave, I have:

 

void main(void) {
  volatile byte data;
 
  PLL_init();        // set system clock frequency to 24 MHz  
 
  // Initialization of SPI communication as a slave 
  SPI0BR =  0b00000011; // baud rate = 24MHz/16=1.5MHz ; 0x53;
  SPI0CR2 = 0b00000010; // SPI0CR2_SPISWAI=1 : Stop SPI clocks when in wait mode
  SPI0CR1 = 0b01000110; // SPI enable ; slave mode ; CPHA=1 ;

                                                // SSOE=1(SS input with MODF feature) ;
  DDRS &= 0x00;
  DDRS |= 0x10;      // 0b 0 0 0 1 0 0 0 0  : PS7(SS), PS6(SCK), PS5(MOSI) are input

                                  // and PS4(MISO) is an output
 
  for( ; ; ) {                   
    // Receive a byte from the master
    //while(SPI0SR_SPTEF == 0); // wait until SPI0DR becomes empty
    //SPI0DR = 0x00;                        // clear the SPI0SR_SPTEF flag
    while(SPI0SR_SPIF == 0);      // wait until the end of an SPI transfer.
    data = SPI0DR;                         // Reads the byte and clears the flag SP0SR_SPIF.

         
    // Send a byte to the master   
    if( data == 0x04){
      while(SPI0SR_SPTEF == 0); //Wait until SPIDR becomes empty.
      SPI0DR = 0x08;
    }else{
      while(SPI0SR_SPTEF == 0); //Wait until SPIDR becomes empty.
      SPI0DR = 0x80;
    }   
    while(SPI0SR_SPIF == 0);    // wait until the end of an SPI transfer. 
    data = SPI0DR;                       // dummy reading to clear SPI0SR_SPIF flag.   
  }
}

 

Running these codes, it seems like that the execution hangs at some of the while loops waiting for the SPIF flag to be set (although I am not very sure). 

 

Alternatively,  I also tried to run the programs by assigning CPHA=0 for both master and slave, and use the PTS_PTS0 bit to bring the slave's SS line to down and then up (you can see this from the commented instructions in the code for the master), but even this trial didn't give positive results.

 

I'd appreciate if anyone could help me.

 

Thanks,

Jaeyun

Labels (1)
0 Kudos
Reply
1 Solution
1,878 Views
bigmac
Specialist III

Hello,

 

I see that you have now corrected your wait loops so that they wait until each flag becomes set.  This would have been the cause of many of your previous problems.  However, the operation of your code should be much easier to understand if you were to create a general SPI transfer function, particularly for the master.  Maybe similar to the following:

 

#define SPI_send(x)  (void)SPI_trans(x)  // Ignore return
#define SPI_rcv()    SPI_trans(0)        // Send dummy byte


byte SPI_trans( byte val){   while(!(SPI0SR & SPI0SR_SPTEF_MASK)); // Wait until ready to send   SPI0DR = val;                         // Send SPI byte    while(!(SPI0SR & SPI0SR_SPIF_MASK) ); // Wait for transfer complete      return SPI0DR;                        // Also clears flag}  

 Your master code might then have the following appearance:

 

SPI_send( 0x40);
data = SPI_rcv();

 

Yes, it is likely that you would need a small delay between these two statements, sufficient for the slave end to process the received byte and to write the required return value to the SPI0DR register, all prior to the first clock edge generated by the master for the second byte transfer.  The delay must also take into account the possible occurrence of interrupts at the slave MCU.

 

Since the slave returns only a single byte at a time, it is unnecessary to test the SPTEF flag - this would be required only if you were queuing two return bytes, making use of the double buffer capability.  The basic slave procedure would be:

 

  1. Wait until SPIF flag is set.
  2. Read SPI0DR
  3. Write return byte to SPI0DR

 

Usually the SPI interrupt would be used for the slave, to allow other tasks to execute while waiting for SPIF flag.  Should you need to return a data sequence, the use of double buffering would ease the master delay requirement after the return of the first byte.

 

Regards,

Mac

 

 

View solution in original post

0 Kudos
Reply
9 Replies
1,878 Views
Lundin
Senior Contributor IV

- Your flag clearing doesn't make any sense, you are sending out various junk data when you try to clear the flags. Clear them once when you send by reading SPI0SR then writing to SPIDR, and once when you receive by reading from SPI0SR, then reading from SPI0DR. This is likely the main reason why the program isn't working.

 

- On S12, peripherals like SPI take precedence over GPIO. Meaning that if you have enabled SPI on a certain port, all port I/O on those 4 pins will be disabled. You don't have to set DDR and PORT registers.

 

- SSOE=1 means that the master MCU will automatically set the SS line low whenever data is sent.The slave will not care about the SSOE bit at all.

 

- If SSOE=0 on the master, the SPI module will release its grasp over the SS pin and you can use it for GPIO instead. This is useful if you wish to perform the SS manually. You will then need to use DDRS and PTS registers as you attempt in your code.

 

- Although, SPI0 is found on PTS pins/bits 4,5,6,7. You are fiddling around with I/O on PTS0. Check the manual to find the SPI pins.

 

- CPHA and CPOL settings don't matter at all, as long as they are the same on both master and slave.

 

- You should consider removing all binary/hex junk from the code and use named bit masks so that humans can read it. Also, the binary 0b notation will not compile on a C compiler.

 

 

0 Kudos
Reply
1,878 Views
admin
Specialist II

Lundin, thanks for your reply.

 I did some changes following Lundin's comments, but I am still having issues.

 

Here I am trying to send a byte from master to slave, and then the slave sends another byte whose value depends on if the reception of the byte sent from the master was satisfactory or not. I can see that the slave receives correctly the the byte sent from the master, but the master does not get the right byte (sent from the slave), but it always receives 0xff. 

I also probed SCK, MOSI, MISO lines using an oscilloscope. SCK and MOSI look fine, but MISO is always high.

 

Does this problem have to do with delays? I haven't used any delay in my code.

 

I'd appreciate if anyone could help me.

 

Below is attached my latest code.

 

Thanks,

 

Jaeyun

 

// Connections:
// MASTER                            SLAVE
// --------------------------------------------------
// PS4 (MISO)  <------------   PS4(MISO)
// PS5 (MOSI)  ------------>   PS5(MOSI)
// PS6 (SCLK) ------------>   PS6(SCLK)
//                                             PS7(SS) <-- GND
// GND              -------------   GND

 

 

// Master

 

void main(void) {
  volatile byte data, status; 
 
  PLL_init();  // set system clock frequency to 24 MHz

  // SPI communication initialization as a master
  SPI0BR  = 0x53; // Baud rate = 250kHz  
  SPI0CR2 = 0b00000000; 

  SPI0CR1 = 0b11010100; // Enable SPI ; Master mode ; CPHA = 1

  
  for( ; ; ){    
                   
    // Send a byte to the slave
    while(!(SPI0SR & SPI0SR_SPTEF_MASK)); //Wait until SPIDR becomes empty.
    // while( SPI0SR_SPTEF == 0); // Wait until SPIDR becomes empty.
    SPI0DR = 0x40;             // output a byte through SPI communication. 
    while(!(SPI0SR & SPI0SR_SPIF_MASK) ); //  Wait until the end of an SPI transfer.   
    //status = SPI0SR;           // to clear the flags
    data = SPI0DR;             // Read or write to clear SPI0SR_SPIF flag        
    
    // Receive a byte from the slave
    while(!(SPI0SR & SPI0SR_SPTEF_MASK)); //  Wait until SPIDR becomes empty.
    SPI0DR = 0x00;             // Read SPI0DR to clear the SPI0SR_SPTEF flag
    while(!(SPI0SR & SPI0SR_SPIF_MASK) ); //  Wait until the end of an SPI transfer.   
    //status = SPI0SR;           // to clear the flags
    data = SPI0DR;             // Write the received data to data                    
  }
}

 

 

//SLAVE

 

void main(void) {
  volatile byte data,status, dummy;
 
  PLL_init();        // set system clock frequency to 24 MHz   
  
  // Initialization of SPI communication as a slave 
  SPI0BR =  0x53; // baud rate = 250kHz 
  SPI0CR2 = 0b00000000; 

  SPI0CR1 = 0b11000100; // SPI enable ; slave mode ;  
   
  for( ; ; ) { 
    // Receive a byte from the master
    while(!(SPI0SR & SPI0SR_SPTEF_MASK)); //  Wait until SPIDR becomes empty.
    //status = SPI0SR;            // to clear the flag SPI0SR_SPIF
    SPI0DR = 0x00;            // write some dummy value to SPI0DR to clear the SPI0SR_SPTEF flag
    while(!(SPI0SR & SPI0SR_SPIF_MASK) ); //  Wait until the end of an SPI transfer.   
    //status = SPI0SR;            // to clear the flag SPI0SR_SPIF
    data = SPI0DR;              // Reads the byte and clears the flag SPI0SR_SPIF.    
          
    // Send a byte to the master   
    if( data == 0x40){
      while(!(SPI0SR & SPI0SR_SPTEF_MASK)); // Wait until SPIDR becomes empty.
      data = 0x04;
    }else{
      while(!(SPI0SR & SPI0SR_SPTEF_MASK)); //  Wait until SPIDR becomes empty.
      data = 0x01;
    }   
    SPI0DR = data; 
    while(!(SPI0SR & SPI0SR_SPIF_MASK) ); // Wait until the end of an SPI transfer.   
    //status = SPI0SR;            // to clear SPI0SR_SPIF flag
    dummy = SPI0DR;              // dummy reading to clear SPI0SR_SPIF flag.        
  }
}

0 Kudos
Reply
1,879 Views
bigmac
Specialist III

Hello,

 

I see that you have now corrected your wait loops so that they wait until each flag becomes set.  This would have been the cause of many of your previous problems.  However, the operation of your code should be much easier to understand if you were to create a general SPI transfer function, particularly for the master.  Maybe similar to the following:

 

#define SPI_send(x)  (void)SPI_trans(x)  // Ignore return
#define SPI_rcv()    SPI_trans(0)        // Send dummy byte


byte SPI_trans( byte val){   while(!(SPI0SR & SPI0SR_SPTEF_MASK)); // Wait until ready to send   SPI0DR = val;                         // Send SPI byte    while(!(SPI0SR & SPI0SR_SPIF_MASK) ); // Wait for transfer complete      return SPI0DR;                        // Also clears flag}  

 Your master code might then have the following appearance:

 

SPI_send( 0x40);
data = SPI_rcv();

 

Yes, it is likely that you would need a small delay between these two statements, sufficient for the slave end to process the received byte and to write the required return value to the SPI0DR register, all prior to the first clock edge generated by the master for the second byte transfer.  The delay must also take into account the possible occurrence of interrupts at the slave MCU.

 

Since the slave returns only a single byte at a time, it is unnecessary to test the SPTEF flag - this would be required only if you were queuing two return bytes, making use of the double buffer capability.  The basic slave procedure would be:

 

  1. Wait until SPIF flag is set.
  2. Read SPI0DR
  3. Write return byte to SPI0DR

 

Usually the SPI interrupt would be used for the slave, to allow other tasks to execute while waiting for SPIF flag.  Should you need to return a data sequence, the use of double buffering would ease the master delay requirement after the return of the first byte.

 

Regards,

Mac

 

 

0 Kudos
Reply
1,878 Views
admin
Specialist II

Thanks bigmac for your reply.

 

I tried bigmac's suggestion. Again, the communcation from Master to Slave is fine, but I am having issues in the opposite direction. I am getting 0xff as of the received data by the master, whereas the data that the slave supposedly sends is 0x04. Again, the MISO line is always high. It seems like it doesn't matter what data the slave sends to the master. The master always receives 0xff.

 

From other posting threads I found that the slave should have assigned a data to SPI0DR before the master starts the communication. I think I am missing this part in my code. I'd appreciate if anyone could help me out with the part of slave sending data to the master.

 

Below is my latest code (after following bigmac's suggestion)

 

Thanks in advance,

Jaeyun

 

---------------------------------------------------------------------------------

// Connections:
// MASTER                     SLAVE
// -------------------------------------
// PS4 (MISO) <------------   PS4(MISO)
// PS5 (MOSI) ------------>   PS5(MOSI)
// PS6 (SCLK) ------------>   PS6(SCLK)
//                            PS7(SS) <-- GND
// GND        -------------   GND

---------------------------------------------------------------------------------

 

Code for Master:

 

---------------------------------------------------------------------------------

#define SPI_send(x) (void)SPI_trans(x) // Ignore return
#define SPI_rcv()   SPI_trans(0)       // Send dummy byte

byte SPI_trans( byte val){
  while(!(SPI0SR & SPI0SR_SPTEF_MASK)); // Wait until ready to send
  SPI0DR = val;                         // Send SPI byte
  while(!(SPI0SR & SPI0SR_SPIF_MASK));  // Wait for transfer complete
  return SPI0DR;                        // Also clears flag
}
 
void main(void) {
  volatile byte data; 
 
  PLL_init();  // set system clock frequency to 24 MHz
  
   // SPI communication initialization as a master
  SPI0BR  = 0x53;                  // Baud rate = 250kHz 
  SPI0CR2 = 0b00000000;
  SPI0CR1 = 0b01010100; // Enable SPI ; Master mode ; CPHA = 1

 
  for( ; ; ){ 
                     
    // Send a byte to the slave
    SPI_send(0x40);             
    ms_delay(100);

  

  // receive a byte from the slave   
    data = SPI_rcv();        
    ms_delay(100);    
   }    
}

---------------------------------------------------------------------------------

 

Code for slave:

 

---------------------------------------------------------------------------------

#define SPI_send(x) (void)SPI_trans(x)   // Ignore return
#define SPI_rcv()   SPI_trans(0)         // Send dummy byte

 

byte SPI_trans( byte val){
   while(!(SPI0SR & SPI0SR_SPTEF_MASK)); // Wait until ready to send
   SPI0DR = val;                         // Send SPI byte
   while(!(SPI0SR & SPI0SR_SPIF_MASK));  // Wait for transfer complete
   return SPI0DR;                        // Also clears flag
}

 

void main(void) {
   volatile byte data;

   PLL_init();        // set system clock frequency to 24 MHz
     

   // Initialization of SPI communication as a slave
   SPI0BR =  0x53;                  // baud rate = 250kHz
   SPI0CR2 = 0b00000000;
   SPI0CR1 = 0b01000100; // SPI enable ; slave mode ; CPHA = 1

 

   for( ; ; ) {
                   
       // Receive a byte from the master
       while(!(SPI0SR & SPI0SR_SPIF_MASK));  // Wait for transfer complete
       data = SPI0DR;                        // Also clears flag                 
       ms_delay(100);

 

       // Send a byte to the master
       if( data == 0x40 ){
        data = 0x04;
       }else{
        data = 0x01;
       }
      
       SPI_send(data);               
       ms_delay(100);
   }
}

0 Kudos
Reply
1,878 Views
admin
Specialist II

Hello,

 

Now I have the problem fixed.

I should have mentioned this from the beginning, but the two MC9S12DG256's are mounted on some commercial boards named Dragon Board 12 Plus. This board has PCB traces that connect  pins of a DAC chip to the pins of SPI0 module of the microcontroller, and it seems like the MISO line is always pulled up.

 

Therefore, the last code that I posted but using this time registers of SPI1 (instead of those that correspond to SPI0) worked fine.

 

Before I leave, I'd like to give thanks to Ludin, BigMac and Oscar.

 

Thanks,

 

Jaeyun

 

 

0 Kudos
Reply
1,878 Views
bigmac
Specialist III

Hello Jaeyun,

 

I'm glad that you solved your hardware problem.  However, I think I should briefly mention that the function and macros that I previously posted were never intended for slave usage, only for the master.  Further, the added delay would be required only for the master.  Placing additional delay within the slave code would be counter-productive - the fastest data turn-around is required.  The following code might have been expected for the slave.

 

// Receive a byte from the masterwhile (!(SPI0SR & SPI0SR_SPIF_MASK));  // Wait for incoming datadata = SPI0DR;                         // Also clears flag// Send return byte to the masterif( data == 0x40)  SPI0DR = 0x04;else               SPI0DR = 0x01;

 

 

This code assumes that the COP timer is inoperative, othwise the timer will require to be cleared within the wait loop, to prevent COP timeout.

 

Regards,

Mac

 

0 Kudos
Reply
1,878 Views
admin
Specialist II

Hello Mac,

 

Thanks for your comments (otherwise I wouldn't have understood correctly how SPI works).

 

I just implemented what you said in your previous posting, but now I see someting that I did not observe when I used the code that I posted lastly. The boards (Dragon-12 Plus) that I am using have LCD displays. The master's LCD displays both transmitted and received data. The slave's LCD does the same thing (it shows the received and trasmitted data). Now, the displayed transmitted data from the master is always the same (0x40) while the displayed receiced data by the slave, the transmitted by the slave and received by the master have two different values: whatever the expected value is (0x40 or 0x04) and 0x00. 0x00 is something not intended. I could place an if statement to ignore the 0x00 value and consider only any nonzero value, but I hesitate to do so because I could also be interested in considering the zero value.

What could I do?

 

Code are followed:

 

Master:

 

for( ; ; ){ 
    

    data = 0x40;                 
    SPI_send(data);
           
    set_lcd_addr(0x00);
    type_lcd("Tx: ");
    set_lcd_addr(0x04);
    write_int_lcd((int)data);
   
    ms_delay(DELAY);

 

    // receive a byte from the slave   
    data = SPI_rcv();
   
    set_lcd_addr(0x40);
    type_lcd("Rx: ");
    set_lcd_addr(0x44);
    write_int_lcd((int)data);              
   
    ms_delay(DELAY);
        

}    

 

 

Slave

 

 for( ; ; ) {                
       // Receive a byte from the master
       while(!(SPI1SR & SPI1SR_SPIF_MASK));  // Wait for transfer complete
       data = SPI1DR;                        // Also clears flag

      
       set_lcd_addr(0x00);
       type_lcd("Rx: ");
       set_lcd_addr(0x04);
       write_int_lcd((int)data);
   

       if (data == 0x40)    data = 0x04;

       else                         data = 0x01;

   
       SPI1DR = data;

       
       set_lcd_addr(0x40);
       type_lcd("Tx: ");
       set_lcd_addr(0x44);
       write_int_lcd((int)data);       
   }

 

Thanks in advance,

Jaeyun

 

0 Kudos
Reply
1,878 Views
bigmac
Specialist III

Hello Jaeyun,

 

Does this code snippet do what you require?  The following code -

 

if (data == 0x40)    data = 0x04;else                 data = 0x01;SPI1DR = data;

 

to be replaced with -

 

switch (data) {case 0x40:   SPI0DR = 0x04;   break;case 0x00:   break;       // Do nothing at present timedefault:   SPI0DR = 0x01;}

 

A further comment - since writing to a LCD device is usually a very slow process, the above code should be executed prior to commencing any display write, so that return data is more immediately available for interrogation by the master(and lessening the delay requirement within the master).

 

Regards,

Mac

 

0 Kudos
Reply
1,878 Views
JCRib
Contributor II

Hello,

 

 

I'm using a MCF51JM, 12Mhz bus, 6Mhz SPI.


 

For high speed writting you need to check only for TEF cause RF takes too long. But then, you need to clean up dirty bytes...

 

void SPI_WriteHS(void* ptr, Word bytes){  Byte* p = (Byte*) ptr;      while(bytes--)   {        while(!SPI1S_SPTEF);    SPI1DL = *p++;          }        while(!SPI1S_SPTEF);   //clean up  while(!SPI1S_SPRF);  SPI_Temp = SPI1DL;}

 

NOTE: Clean up can miss the last byte depending on bus/spi clock

 

 

I think this is a workaround, cause RF should be activated after TEF, but it wastes 50% of the time!!

 

Why RF flag takes so long to be activated?

 

 


Best regards,

 

Jose Ribeiro

 

0 Kudos
Reply