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):
SPI_MR
:MSTR
= 1PS
= 0PCSDEC
= 0PCS
= 0111DLYBCS
= 0
SPI_CSR[3]
:CPOL
= 0NCPHA
= 1CSAAT
= 0BITS
= 0000SCBR
= 20DLYBS
= 0DLYBCT
= 0
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:
- What's causing the SPI peripheral to become disabled on the write to
SPI_TDR
? Should I un-comment the line in
SPI_Write()
that reads theSPI_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); }
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 (inSPI_Write()
), then the code works perfectly and the communications succeeds.
Useful links:
- AT91SAM7X Series Preliminary.pdf
- ATMEL software package/library
- spi.c from Atmel's library
- spi.h from Atmel's library
An example of initializing the SPI and performing a transfer of 5 bytes is highly appreciated and helpful.