tags:

views:

106

answers:

0

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.