2

My AT91SAM7X512's SPI peripheral gets disabled on the X time (X varies) that I write to SPI_TDR. As a result, the processor hangs on the while loop that checks the TDRE flag in SPI_SR. This while loop is located in the function SPI_Write() that belongs to the software package/library provided by ATMEL. The problem occurs arbitrarily - sometimes everything works OK and sometimes it fails on repeated attempts (attemp = downloading the same binary to the MCU and running the program).

Configurations are (defined in the order of writing):

  1. SPI_MR:
    • MSTR = 1
    • PS = 0
    • PCSDEC = 0
    • PCS = 0111
    • DLYBCS = 0
  2. SPI_CSR[3]:
    • CPOL = 0
    • NCPHA = 1
    • CSAAT = 0
    • BITS = 0000
    • SCBR = 20
    • DLYBS = 0
    • DLYBCT = 0
  3. SPI_CR:
    • SPIEN = 1

After setting the configurations, the code verifies that the SPI is enabled, by checking the SPIENS flag.

I perform a transmission of bytes as follows:

const short int dataSize = 5;
// Filling array with random data
unsigned char data[dataSize] = {0xA5, 0x34, 0x12, 0x00, 0xFF};
short int i = 0;
volatile unsigned short dummyRead;

SetCS3();   // NPCS3 == PIOA15
while(i-- < dataSize) {
    mySPI_Write(data[i]);
    while((AT91C_BASE_SPI0->SPI_SR & AT91C_SPI_TXEMPTY) == 0);
    dummyRead = SPI_Read(); // SPI_Read() from Atmel's library
}
ClearCS3();
/**********************************/
void mySPI_Write(unsigned char data) {
    while ((AT91C_BASE_SPI0->SPI_SR & AT91C_SPI_TXEMPTY) == 0);
    AT91C_BASE_SPI0->SPI_TDR = data;
    while ((AT91C_BASE_SPI0->SPI_SR & AT91C_SPI_TDRE) == 0); // <-- This is where
    // the processor hangs, because that the SPI peripheral is disabled
    // (SPIENS equals 0), which makes TDRE equal to 0 forever.
}

Questions:

  1. What's causing the SPI peripheral to become disabled on the write to SPI_TDR?
  2. Should I un-comment the line in SPI_Write() that reads the SPI_RDR register?
    Means, the 4th line in the following code: (The 4th line is originally marked as a comment)

    void SPI_Write(AT91S_SPI *spi, unsigned int npcs, unsigned short data)
    {
        // Discard contents of RDR register
        //volatile unsigned int discard = spi->SPI_RDR;
        /* Send data */
        while ((spi->SPI_SR & AT91C_SPI_TXEMPTY) == 0);
        spi->SPI_TDR = data | SPI_PCS(npcs);
        while ((spi->SPI_SR & AT91C_SPI_TDRE) == 0);
    }
    
  3. Is there something wrong with the code above that transmits 5 bytes of data?

Please note:

  • The NPCS line num. 3 is a GPIO line (means, in PIO mode), and is not controlled by the SPI controller. I'm controlling this line by myself in the code, by de/asserting the ChipSelect#3 (NPCS3) pin when needed. The reason that I'm doing so is because that problems occurred while trying to let the SPI controller to control this pin.
  • I didn't use the PDC/DMA controller and prefer not using it.
  • I didn't reset the SPI peripheral twice, because that the errata tells to reset it twice only if I perform a reset - which I don't do. Quoting the errata:

    If a software reset (SWRST in the SPI Control Register) is performed, the SPI may not work properly (the clock is enabled before the chip select.)
    Problem Fix/Workaround
    The SPI Control Register field, SWRST (Software Reset) needs to be written twice to be cor- rectly set.

  • I noticed that sometimes, if I put a delay before the write to the SPI_TDR register (in SPI_Write()), then the code works perfectly and the communications succeeds.

Useful links:

An example of initializing the SPI and performing a transfer of 5 bytes is highly appreciated and helpful.

Bwooce
  • 2,123
  • 19
  • 28
Dor
  • 7,344
  • 4
  • 32
  • 45
  • Did you resolve this Dor? We'd be interested to know if you found the root cause. – Bwooce Feb 13 '11 at 09:12
  • I don't remember how I solved the problem. I think that I had a problem in the initialization process of the processor clock: the steps of init weren't as written in the datasheet. – Dor Mar 17 '11 at 21:15
  • Notably, this code doesn't "transmit five bytes of data". – Ben Voigt Mar 15 '13 at 14:18

2 Answers2

1

You use

while(i-- < dataSize)

decrementing a signed integer i, not incrementing it and accessing data[i] until i overflows to a positive value. Who knows what happens, what memory or registers you are accessing? Using unsigned integers is generally better if you do not need to hold negative values.

Also, you are already checking for TXEMPTY which implies the data is moved to shift register AND sent out. So, you don't need to check TDRE also, which implies the data is moved to shift register but may not be sent out.

Also, I strongly recommend to use PDC if you are not exchanging data simultaneously, which is not the case I think.

obareey
  • 309
  • 2
  • 9
0

Well, I'm almost 13 years later but I happened to hit the same issue.

The SPI can get disabled automatically (SPIENS == 0) due to Mode Fault Detection:

A mode fault is detected when the SPI is programmed in Master mode and a low level is driven by an external master on the NPCS0/NSS signal.

[...]

When a mode fault is detected, the SPI_SR.MODF bit is set until SPI_SR is read and the SPI is automatically disabled until it is re-enabled by writing a 1 to the SPI_CR.SPIEN bit.

The fault detection appears to be triggered due to driving the Chip Select manually using PIO.

Assuming the system is NOT multi-master, which makes the fault detection unnecessary, the fix is to disable it:

//Avoid fault due to CS driven through PIO    
spi_disable_mode_fault_detect(SPI);
Zmaster
  • 1,095
  • 9
  • 23