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
Solved! Go to Solution.
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:
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
- 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.
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.
}
}
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:
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
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);
}
}
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
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
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
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
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