AT91SAM7X512's SPI peripheral gets disabled on write to SPI_TDR

Thread Starter

dor

Joined Feb 20, 2009
63
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:
Rich (BB code):
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);
    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)
    Rich (BB code):
    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.

Thank you.

Best regards,
Dor.
 
Top