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.
Solved! Go to Solution.
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.
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
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.
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?
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.
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.
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
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.
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
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).
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.
My personal preference would be the second method.
Regards,
Mac
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.
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
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.
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
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;}
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
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.
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
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.
Once again many thanks for everyone's input.