One Way SPI Communications

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

One Way SPI Communications

Jump to solution
3,687 Views
Coderunner
Contributor III

Hello everyone,

 

I've been fighting all day trying to get SPI to work to no avail. I am trying to use SPI1 Module on MC9S08JM60 to send data to a driver (pretty much acts like a serial to parallel out shift register). So I want to use SPI to send data and use its clock. Shouldn't I be able to send data to a non-spi device? Can't SPI be configure one-way, to just send out data and not care about any acknowlegement? Any ways here is the my code involving SPI:

 

 

void SPIsend(unsigned char data);unsigned char mydata=11;        //test data//*** MAIN *********************************************************void main(void) {   //*** Initialize SPI  SOPT2_SPI1FE = 0;             //disable input filter on SPI1 to allow higher SPI baud rate  SPI1C1 = 0x00;                //disable SPI1 Module  SPI1C2 = 0x49;                //16-bit mode, MOSI=Master I/O  SPI1BR = 0x00;                //set baud rate; prescaler divisor = 1, baud rate divisor = 2  SPI1S |= (unsigned char)0x40; //read-modify-write operation of the SPI1S registr to clear the MODF and SPIMF flags  SPI1M = 0x00;                 //clear data buffer  SPI1C1 = 0x54;                //enable SPI1,disable SPI Ints, clock active high, data MSB first    //*** FOREVER LOOP  for(;;)   {    __RESET_WATCHDOG();         //feeds the dog        SPIsend(mydata);  } }//*** END MAIN//*** SEND DATA USING SPI FUNCTIONvoid SPIsend(unsigned char data){  while(!SPI1S_SPTEF);          //wait until transmit buffer is empty  SPI1D=data;                   //write to Tx data buffer; start to send data  SPI1S;                        //read status register  return;}

 I get nothing on the SPI output (MOSI pin). Am I not initializing something right? I don't have to use SS right? Any help would be appreciated.

Labels (1)
0 Kudos
1 Solution
1,758 Views
peg
Senior Contributor IV

Hello

I suspect the problem is that you are not actually doing a 16-bit write to the data register.

Probably the easiest way to test this is to turn off 16-bit mode in your initialisation and see what happens.

 

View solution in original post

0 Kudos
20 Replies
1,758 Views
bigmac
Specialist III

Hello,

 

In 16-bit mode, a coherency mechanism prevents the sending of data until both high and low bytes are written.  Your current code would write only the high byte.  The solution would be to change the data parameter to an unsigned int.

 

I can't see the need to read the status register immediately after the write since you are not waiting for the SPRF flag.

 

Regards,

Mac

 

0 Kudos
1,759 Views
peg
Senior Contributor IV

Hello

I suspect the problem is that you are not actually doing a 16-bit write to the data register.

Probably the easiest way to test this is to turn off 16-bit mode in your initialisation and see what happens.

 

0 Kudos
1,758 Views
Coderunner
Contributor III

Doh! That, AND I wasn't writing to the correct data register. Should be SPI1D16 if in 16 bit mode. Thank you.

 

I have another question. How can I send 12-bit data over 16-bit SPI. Do I shift the data to eliminate 1 byte gaps?

0 Kudos
1,758 Views
peg
Senior Contributor IV

Hi,

Generally slave SPI devices are aware that masters can only send 8 or 16 bits at a time and so if they only require, say, 12-bits then they just accept the first 12 and ignore the next 4. Sometimes it is neccessary to set those unused bits all high or all low. Either way there is no way to generate SPI clock cycles in anything other than multiples of 8.

0 Kudos
1,758 Views
Coderunner
Contributor III

Hello Peg,

 

But what if you're trying to drive a divice such as TLC5947? I has a serial data input (non-SPI) and I doubt it knows enough to disregard those zeros.

0 Kudos
1,758 Views
bigmac
Specialist III

Hello,

 

The TLC5947 device has 24 channels of 12-bit data.  Consequently it contains a 288-bit shift register.  Using the SPI module, this should be handled as 36 bytes, or 18 words of data, with each byte or word suitably packed to reflect the 12-bit channel data.  The packing process will probably be simpler for byte transfer, rather than word transfer.

 

Regards,

Mac

 

0 Kudos
1,758 Views
Coderunner
Contributor III

Yeh, I was thinking that would be the way to do it. I've seen others do it that way. I was just wondering if there was a way to modigy SPI hardware to do 12-bit data, buy I guess that was wishfull thinking. Thanks.

0 Kudos
1,758 Views
bigmac
Specialist III

Hello,

 

Perhaps something like the following function could be used to transfer the data for all 24 channels.  It assumes that the data is contained in a word array, and the value contained at each position of the array does not exceed 4095 decimal.  Four channels are packed into each three words sent via SPI.

 

void update_all( word *chan){  signed char n;  for (n = 20; n >= 0; n -= 4) {    SPIsend16( (chan[n+3] << 4) | (chan[n+2] >> 8));    SPIsend16( (chan[n+2] << 8) | (chan[n+1] >> 4));    SPIsend16( (chan[n+1] << 12) | chan[n]);  }  // Strobe data latch here}

 

Regards,

Mac

 

0 Kudos
1,758 Views
Coderunner
Contributor III

Yeh, building a data array first and then sending seems to be the way. It's starting to somewat work.

My current problem is that in my code I latch after I'm done sending the 288 bits (24 channels) but in reality the latch happens  before the data is finished sending (actually the last channel is cut off).

0 Kudos
1,758 Views
bigmac
Specialist III

Hello,

 

The cause of your current problem is that the SPI transfer is not complete until the SPRF flag is set, following the send of the final word.  Because you are not waiting for this to occur before exiting the SPI send function, the strobe pulse will occur prematurely.

 

You have two choices.

  1. Delay for a sufficient period, after sending the final word, before generating the strobe pulse, or
  2. Modify the SPI send function so that you wait until the SPRF flag becomes set, and then clear the flag before exiting the function.  A simple while loop followed by a read of the data register will give this functionality.

My personal preference would be the second method.

 

Regards,

Mac

 

0 Kudos
1,758 Views
rocco
Senior Contributor II

A third option, the one I use, is to have a "WaitForSpiDone" function that simply waits for the SPRF flag to set, and then call that function prior to the function that issues the strobe.

0 Kudos
1,758 Views
bigmac
Specialist III

Hello Rocco,


rocco wrote:

A third option, the one I use, is to have a "WaitForSpiDone" function that simply waits for the SPRF flag to set, and then call that function prior to the function that issues the strobe.


I can see a potential problem with this method.

 

Since the SPRF flag will already be set from the completion of transfers prior to the final word or byte, it must be immediately cleared on entry to the function, so that the completion of the final send can be detected.  Should an interrupt occur somewhere after the initiation of the final send, but prior to the clearing of the flag, it is possible that this transfer may complete while the interrupt processing is in progress.  If this were to occur, the wait function would never exit.

 

This issue will not occur if the flag is cleared within the send function, but to do this the function must wait until the flag is set.

 

Regards,

Mac

 

0 Kudos
1,758 Views
rocco
Senior Contributor II

 

Hi Mac,

bigmac wrote:
Since the SPRF flag will already be set from the completion of transfers prior to the final word or byte, it must be immediately cleared on entry to the function, so that the completion of the final send can be detected.  Should an interrupt occur somewhere after the initiation of the final send, but prior to the clearing of the flag, it is possible that this transfer may complete while the interrupt processing is in progress.  If this were to occur, the wait function would never exit.

 

Yes, I can see that problem and then wondered why I am not experiencing it.

 

Going back into my notes, I noticed that there is no good way to tell when the SPI is done. The Transmitter-Empty flag will always be set one transfer early, and the Receiver-Full flag will be clear once the data is read.

 

That routine works for me because my receiver ISR does not read the last byte, but simply masks the interupt, thereby insuring that there is a flag to test. That's a pretty narrow implimentation, that works only because my SPI is only transmitting, not receiving.

 

In some older code, I just spin in a small wait routine (which I hate). The wait routine looks at the SPI baud-rate bits to determine how long. At least that is more portable.

0 Kudos
1,758 Views
bigmac
Specialist III

Hello Rocco,

 

If you were using SPI interrupt on the SPRF flag this puts a different complexion on the operation.  When the interrupt occurs the transmission should already be complete, with the SPRF flag set.  If the ISR is aware of the final byte in a sequence, the strobe pulse could be generated by the ISR code, without the need for a wait loop elsewhere.

 

Having a short timeout loop, in addition to waiting for the SPRF flag to become set, can be relatively simply accomplished with assembly programming.  However, with C programming this tends to be somewhat more complex, so I would usually keep the SPI function as simple as possible.  With normal (non-debug) operation of the SPI, I guess the only reason the flag might fail to become set would be if the SPI configuration "glitched".  Normally a COP reset would eventually occur, to re-initialise the SPI module.

 

If timeout should occur without the flag becoming set, what course of action would you follow?  I have previously been uncertain of the most appropriate action.

 

Regards,

Mac

 

0 Kudos
1,758 Views
Coderunner
Contributor III

The delay solution works but waiting for SPRF to become set does not (latch still happens before transfer is complete). Or I'm doing something wrong.

 

According to datasheet: "SPRF is cleared by reading SPRF while it is set, then reading the SPI data register."

 

 

//*** Function - SPI Send Datavoid SPIsend(unsigned short int data){  while(!SPI1S_SPTEF);          //wait until transmit buffer is empty       SPI1D16=data;                 //write to Tx data buffer; start to send data  while(!SPI1S_SPRF)            //wait for reciever to become full,  SPI1S_SPRF;  SPI1D16;                     
  return;}

 

 

0 Kudos
1,758 Views
bigmac
Specialist III

Hello,

 

Your wait loop is effectively as follows:

while (!SPI1S_SPRF)  SPI1S_SPRF; 

This doesn't make much sense, so the compiler may be messing it.  Perhaps you could try the following generalised function for SPI communications. 

 

unsigned short int SPIsend(unsigned short int data){  while (!SPI1S_SPTEF);       SPI1D16 = data;  while (!SPI1S_SPRF);   // Wait for completion of transfer  return SPI1D16;        // Clears flag}

 

To avoid a compiler warning message when you ignore the return data, use a void cast when you call the function.

 

Regards,

Mac

 

0 Kudos
1,758 Views
Coderunner
Contributor III

bigmac,

 

Your last code example does work - latch happens after all data is sent, BUT this code introduces a small gap between each 8 SPI data bits sent...

 

Thanks,

 

Sergey

 

p.s. Another way, that fixed my early latch problem, was increasing the baud rate of SPI. At higher baud rate SPI was sending data faster and therefore before latch happens.

0 Kudos
1,758 Views
bigmac
Specialist III

Hello Sergey,

 

Since the data is synchronous with the clock pulses generated by the SPI master, any gaps between the sending of individual bytes or words will generally be quite inconsequential.  This is certainly the case for the LED driver peripheral.  The gap represents the time required to assemble the next word value to be sent.  The gap could be even longer if an interrupt were to occur within the send sequence.

 

Regards,

Mac

 

 

 

0 Kudos
1,758 Views
Coderunner
Contributor III

I would like to thank everyone in this forum for helping me out, especially bigmac and peg. I was quite amazed how helpful and fast to respond are people in this forum. Thank you once again.

0 Kudos
1,758 Views
Coderunner
Contributor III

Once again many thanks for everyone's input.

0 Kudos