AT91SAM7X512's SPI peripheral gets disabled on write to SPI_TDR
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
Means, the 4th line in the following code: (The 4th line is originally marked as a comment)SPI_Write()
that reads theSPI_RDR
register?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.
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.
精彩评论