brightside_design wrote:
>--- In lpc2000@yahoogroups.com, Tom Walsh <tom@...> wrote:
>
>
>>brightside_design wrote:
>>
>>
>>
>>>I have a problem with a simple SPI example program.
>>>Part: LPC2129
>>>PCK: 14.7 MHz
>>>Mode: Master, CPOL=0, CPHA=0, LSIF=0.
>>>
>>>I initialise the SPI like this:-
>>>void SPI_Init(void)
>>> {
>>> S0SPCCR = 0x26; // Divides PCK to give about 400kHz
>>> S0SPCR = 0x20; // Select Master mode
>>> }
>>>
>>>And then use the SPI like this:-
>>>void SPI_Transfer( char *buf, int count)
>>>{
>>> int r = 0, i = 0;
>>>
>>> for( ; i < count; i++ )
>>> {
>>> S0SPDR = buf[i]; // Write data
>>> do{ r = S0SPSR; }while(!(r & 0x80));// Wait for SPIF
>>> // Read status - to be done
>>> buf[i] = S0SPDR; // Read data
>>> }
>>>}
>>>
>>>
>>>
>>>
>>>
>>Try writing it this way:
>>
>>void SPI_Transfer( char *buf, int count)
>>{
>>volatile int r = 0;
>>volatile int i = 0;
>> for( ; i < count; i++ )
>> {
>> S0SPDR = buf[i]; // Write data
>> do{ r = S0SPSR; }while(!(r & 0x80));// Wait for SPIF
>> // Read status - to be done
>> buf[i] = S0SPDR; // Read data
>> }
>>}
>>
>>
>>If you look deeper at the underlying assembly language via the
>>
>>
>JTAG, I'm
>
>
>>fairly confident you will find that it reads "r" once, then loops
>>
>>
>on the
>
>
>>resultant value. Essentially, the volatile keyword tells the
>>
>>
>compiler
>
>
>>to assume nothing about the variables' value it holds, to actually
>>
>>
>check
>
>
>>it rather than to optimize the reference.
>>
>>YMMV, but your function would probably work if you turned off all
>>optimizations (-O0).
>>
>>TomW
>>
>>
>>
>>
>>>Sometimes I power up the device and this function runs correctly
>>>with problem whatsoever. Other times the function just hangs. If
>>>
>>>
>I
>
>
>>>attach a JTAG debugger (Keil ULINK) I find that the function is
>>>hanging at the line "do{ r = S0SPSR; }while(!(r & 0x80));" on the
>>>first itteration.
>>>
>>>There is no hardware attached to the SPI port. I figured that the
>>>device should put data out onto an unconnected pin without any
>>>problem and that I should be able to get the software working
>>>
>>>
>before
>
>
>>>thinking about ading hardware.
>>>
>>>Has anyone got any ideas what might be going wrong?
>>>
>>>Thanks,
>>>
>>>Paul
>>>
>>>
>>--
>>Tom Walsh - WN3L - Embedded Systems Consultant
>>http://openhardware.net, http://cyberiansoftware.com
>>"Windows? No thanks, I have work to do..."
>>----------------------------------------------------
>>
>>
>>
>
>Thanks Tom, I will try that when I have a moment but I don't think
>thats the answer. I've just read through the disassemby for that
>function and it is reading the register each time through. Also the
>problem is intermittant, sometimes it works fine, other times it
>hangs.
>
>
>
I have a state machine which is servicing three MAX3100 UARTS on the SPI
circuit. The state machine steps are governed by the SPI interrupt.
Each time an SPI interrupt fires, the state machine advances one step.
The machine runs until all UARTS are "quiet", meaning nothing to
transmit and nothing in the recievers. Then the machine shuts down.
The state machine is driven by the SPI interrupt, however, the initial
"kick-off" of putting the machine into motion is handled by two external
conditions:
1) EXTINT0 is triggered by a wired-OR of the IRQ output of the MAX3100's
(interrupt asserted only on RX data available).
2) Data has been entered into one of the transmit queues. In which
case, the EXTINT0 ISR is soft triggered via VICSoftInt bit.
That is the structure of my ISR and it's relation to the other software
in the system. What I'd found was that this state machine / VICSoftInt
mechanism would run fine for a while, then randomly the SPI ISR would
deadlock looking for the SPI completion bit. The only way I've found
out of that deadlock was to put a timeout in the code which looks at the
completion bit:
================= begin spiISR() ==============
void spi0ISR (void)
{// state machine to service the uarts.
volatile ushort byt;
for (byt=0x80; byt; byt--) if (SPSR & 0x80) break;
switch (spiState) {
/////////////////////////////////////////
// handler for serial2
/////////////////////////////////////////
case Srx2step0: // begin the walk down all three uarts.
// assume shutdown in final state...
// but any activty will force a new cycle to run.
AllIsQuietOnSpi = True;
dataWord = getS2DataTosend();
// send first byte on its way.
IOCLR = SEL2_BIT;
spiState = Srx2step1;
SPDR = (dataWord >> 8);
break;
...
================== snip ================
This works for me. I don't like it, but it works. I'd spent several
days trying to work out why the SPI would deadlock and could not find
any explanation. What I did find out is that the SPI interrupt would
fire well before the SPIF completion bit was set!!!
This was proven by examining the value of the "byt" variable in my above
routine. It always showed that a significant number of loops occurred
before the SPIF bit was seen. There were random intervals in which the
for() loop would expire to full count (zero). After some testing, I
arrived at the value of 0x80 as the loop value which was twice the worst
case delay count awaiting the SPIF bit (when it did not goto zero).
So, I would arrive at the spiISR service and have to wait for the
completion bit. Sometimes there would be an interrupt, but never a
completion bit. I also found that I had to wait until that bit appeared
otherwise if I assumed that because I was in the ISR due to a completed
cycle and simply load the next byte into the SPI data register, the SPI
would sometimes die with a WCOL write collision!
According to the docs, there is absolutely no information nor timing
diagrams detailing the relationship of: the SPIF bit, shift clock, and
interrupts. Again, the Philips docs are sadly lacking in detail on
critical issues (see discussion in this group messages regarding the ADC
leakage).
This is with the LPC2106 processor. Interestingly, the SSI of the
LPC2138 does not suffer from this problem... So, there appears to be an
as yet documented failure / weakness in the LCP2106 SPI controller.
Given that I was three months into this new board, had my first
prototype and was commited to using the Philips part, I had to "deal
with it". Not like I've been here before. :-/
The random deadlock is explained as a race condition between when the
interrupt fired and when I actually reloaded the SPI TX register. Most
times I would be outside of the time between the SPI interrupt and SPIF
flagging, but sometimes I would hit it and lockup the SPI controller.
I cannot explain why the SPIF bit is missed however. That appears to be
a race condition in the silicon where a co-incidence of reading from the
SPI status register vs. the AHB writing into the status register... ???
Regards,
TomW
--
Tom Walsh - WN3L - Embedded Systems Consultant
http://openhardware.net, http://cyberiansoftware.com
"Windows? No thanks, I have work to do..."
----------------------------------------------------