| Welcome to AT91SAM Community Forum http://www.at91.com/samphpbb/ |
|
| SAM7X512's SPI peripheral gets disabled on write to SPI_TDR http://www.at91.com/samphpbb/viewtopic.php?f=15&t=19077 |
Page 1 of 1 |
| Author: | mDor [ Thu Mar 18, 2010 10:51 pm ] |
| Post subject: | SAM7X512'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):
I perform a transmission of bytes as follows: 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[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:
Thank you. Best regards, Dor. |
|
| Author: | dfridley [ Fri Mar 19, 2010 12:35 am ] |
| Post subject: | Re: SAM7X512's SPI peripheral gets disabled on write to SPI_TDR |
Hi Dor, Here is initialization code and spi data writing code that uses the Variable mode of the SPI peripheral. I mangled the names to C style (this is from a CPP project) and you will not be able to drop this in and use it, but, I think you should be able compare it to what you are doing. This is on a system that has 3 SPI peripherals that we communicate to, each on its own CS line. Hope it helps... The standard disclaimer applies! Code: // NOTE: Code excerpts from a project. Will NOT compile directly but to be used as example.
// This code uses the SPI variable mode (i.e. SPI peripheral handles the CS lines) // TYPEDEFS / CLASS DEFINITION ===================================================================== // Normally in a typedefs.h file but included here typedef unsigned char U8; typedef unsigned int U32; // Normally in spi.h file but included here typedef enum { SPI_CS0, SPI_CS1, SPI_CS2, SPI_CS3 }eSpiCs; // DEFINES and CONSTANTS =========================================================================== // Normally in a typedefs.h file but included here const U32 BIT0 = 0x00000001; const U32 BIT1 = 0x00000002; const U32 BIT2 = 0x00000004; const U32 BIT3 = 0x00000008; const U32 BIT4 = 0x00000010; const U32 BIT5 = 0x00000020; const U32 BIT6 = 0x00000040; const U32 BIT7 = 0x00000080; const U32 BIT8 = 0x00000100; const U32 BIT9 = 0x00000200; const U32 BIT10 = 0x00000400; const U32 BIT11 = 0x00000800; const U32 BIT12 = 0x00001000; const U32 BIT13 = 0x00002000; const U32 BIT14 = 0x00004000; const U32 BIT15 = 0x00008000; const U32 BIT16 = 0x00010000; const U32 BIT17 = 0x00020000; const U32 BIT18 = 0x00040000; const U32 BIT19 = 0x00080000; const U32 BIT20 = 0x00100000; const U32 BIT21 = 0x00200000; const U32 BIT22 = 0x00400000; const U32 BIT23 = 0x00800000; const U32 BIT24 = 0x01000000; const U32 BIT25 = 0x02000000; const U32 BIT26 = 0x04000000; const U32 BIT27 = 0x08000000; const U32 BIT28 = 0x10000000; const U32 BIT29 = 0x20000000; const U32 BIT30 = 0x40000000; const U32 BIT31 = 0x80000000; // .c file constants static const U32 SPI_PORT_CS0_BIT = BIT4; static const U32 SPI_PORT_CS1_BIT = BIT5; static const U32 SPI_PORT_CS2_BIT = BIT6; static const U32 SPI_PORT_MISO_BIT = BIT8; static const U32 SPI_PORT_MOSI_BIT = BIT9; static const U32 SPI_PORT_CLK_BIT = BIT10; static const U32 SPI_PERIPH_ID = BIT12; // The chip select lines used when sending data. // These values are loaded into the SPI Transmit Data Register (TDR) when sending data. static const U32 SPI_TXRX_CS0 = BIT19 | BIT18 | BIT17 ; static const U32 SPI_TXRX_CS1 = BIT19 | BIT18 | BIT16; static const U32 SPI_TXRX_CS2 = BIT19 | BIT17 | BIT16; static const U32 SPI_TXRX_CS3 = BIT18 | BIT17 | BIT16; // VARIABLES ======================================================================================= // PROTOTYPES ====================================================================================== // CODE ============================================================================================ /*------------------------------------------------------------------------------ Description Initializes the spi hardware Remarks Uses the microprocessor's SPI1 port ------------------------------------------------------------------------------*/ void Spi_Init( void ) { // Bits used to configure the spi registers const U32 CSR_NCPHA = BIT1; const U32 CSR_CSAAT = BIT3; const U32 MR_MSTR = BIT0; const U32 MR_PS = BIT1; const U32 MR_MODFDIS = BIT4; const U32 CR_SPIEN = BIT0; const U32 CR_SWRST = BIT7; m_pRegs = (SPI_SFRs *)SPI1_BASE; // Assign SPI pins to the peripheral B functions gcPortA.PeriphBOn( SPI_PORT_CS0_BIT | SPI_PORT_CS1_BIT | SPI_PORT_CS2_BIT | SPI_PORT_MISO_BIT | SPI_PORT_MOSI_BIT | SPI_PORT_CLK_BIT ); // Perform SW reset m_pRegs->CR = CR_SWRST; gcPMC.EnablePeriphClock( SPI_PERIPH_ID ); // Setup for the chips: Mode 0, 8 Data bits, baud rate MCK/15, CSAAT m_pRegs->CSR0 = CSR_NCPHA | CSR_CSAAT | (U32)(0xF<<8); m_pRegs->CSR1 = CSR_NCPHA | CSR_CSAAT | (U32)(0xF<<8); m_pRegs->CSR2 = CSR_NCPHA | CSR_CSAAT | (U32)(0xF<<8); // Set the configuration: Master, Disable Mode Fault Detection, Peripheral Select m_pRegs->MR = MR_MSTR | MR_PS | MR_MODFDIS; // Enable the spi m_pRegs->CR = CR_SPIEN; } /*-------------------------------------------------------------------------------------------------- Description Sends a byte on the spi port and returns the byte received Inputs data_ - The data to send spiCs_ - The chip select to use assertCs_ - true: sets the cs line high after sending, false: leaves the cs line low Returns The byte read from the bus Remarks Assumes the spi is NOT set up to use fixed CS's. Sends the byte passed in on the bus and retrieves the byte returned. --------------------------------------------------------------------------------------------------*/ U8 Spi_SendByte(U8 data_, eSpiCs spiCs_, bool assertCs_ ) { U8 returnVal; U32 txData; while( !(m_pRegs->SR & BIT1) ) { ; // transfer compl. wait } txData = (U32)data_; // Load the CS line switch( spiCs_ ) { case SPI_CS0: txData |= SPI_TXRX_CS0; break; case SPI_CS1: txData |= SPI_TXRX_CS1; break; case SPI_CS2: txData |= SPI_TXRX_CS2; break; case SPI_CS3: txData |= SPI_TXRX_CS3; break; default: break; } // Check if the CS line should be asserted after this transfer if( assertCs_ == true ) { txData |= BIT24; } returnVal = (U8)m_pRegs->RDR; m_pRegs->TDR = txData; while( !(m_pRegs->SR & BIT0) ) { ; // wait for char } returnVal = (U8)m_pRegs->RDR; return( returnVal ); } /*-------------------------------------------------------------------------------------------------- Description Example of sending data data over the spi bus Remarks Sends 5 bytes of random data over the spi bus using CS0. When the last byte is sent the CS line is pulled back high. --------------------------------------------------------------------------------------------------*/ void SendDataExample( void ) { // Filling array with random data U8 data[] = {0xA5, 0x34, 0x12, 0x00, 0xFF}; // Loop through the data until the last byte for( U8 index=0; index < (U8)(sizeof(data)-1); index++ ) { // Ignore the return value, and make sure the CS line is low while // sending this packet (void)Spi_SendByte( data[index], SPI_CS0, false ); } // After this last byte is sent pull the CS line high // Also don't care about the return value. (void)Spi_SendByte( data[index], SPI_CS0, true ); } |
|
| Author: | mDor [ Fri Mar 19, 2010 11:38 am ] |
| Post subject: | Re: SAM7X512's SPI peripheral gets disabled on write to SPI_TDR |
Hi dfridley, Thank you for the code! Few things noticed:
In general to everyone: I'd love to see additional code examples that closely resembles to my situation: fixed CS lines, CS#3 is in PIO mode, etc. |
|
| Author: | dfridley [ Fri Mar 19, 2010 1:55 pm ] |
| Post subject: | Re: SAM7X512's SPI peripheral gets disabled on write to SPI_TDR |
Hi Dor, This is for AT91SAM7X512. Take note how we handle the write. Spi_Write() 1) Wait until the TX register is empty (make sure there isn't data waiting to go out) 2) Perform dummy read of RX register (makes sure Bit0 is clear) 3) Send the data to the TX register 4) Wait until a byte is RX'd (if we recieved a byte the TX is complete) 5) Read the RX register and return the value I believe this is different than the way you write bytes. Quote: 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[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. } Try Changing this to: Code: // snip... SetCS3(); // NPCS3 == PIOA15 while(i-- < dataSize) { mySPI_Write(data[i]); dummyRead = AT91C_BASE_SPI0->SPI_RDR; } ClearCS3(); /**********************************/ void mySPI_Write(unsigned char data) { volatile unsigned int dummy; while ((AT91C_BASE_SPI0->SPI_SR & AT91C_SPI_TXEMPTY) == 0); dummy = AT91C_BASE_SPI0->SPI_RDR; AT91C_BASE_SPI0->SPI_TDR = data; while ((AT91C_BASE_SPI0->SPI_SR & AT91C_SPI_TDRE) == 0); } Or you could condense by returning the recieved byte from the write. Then you could recieve data simply by "rxData = mySPI_Write(0)" Code: // snip...
SetCS3(); // NPCS3 == PIOA15 while(i-- < dataSize) { (void)mySPI_Write(data[i]); } ClearCS3(); /**********************************/ unsigned char mySPI_Write(unsigned char data) { unsigned char dummy; // Make sure txer is empty while ((AT91C_BASE_SPI0->SPI_SR & AT91C_SPI_TXEMPTY) == 0); // clear the rx flag by performing a dummy read dummy = AT91C_BASE_SPI0->SPI_RDR; // send the data AT91C_BASE_SPI0->SPI_TDR = data; // wait for reception while ((AT91C_BASE_SPI0->SPI_SR & AT91C_SPI_TDRE) == 0); // return the recieved data dummy = AT91C_BASE_SPI0->SPI_RDR; return dummy; } |
|
| Author: | mDor [ Fri Mar 19, 2010 3:54 pm ] |
| Post subject: | Re: SAM7X512's SPI peripheral gets disabled on write to SPI_TDR |
Hi dfridley, dfridley wrote: Take note how we handle the write. Spi_Write() 1) Wait until the TX register is empty (make sure there isn't data waiting to go out) 2) Perform dummy read of RX register (makes sure Bit0 is clear) 3) Send the data to the TX register 4) Wait until a byte is RX'd (if we recieved a byte the TX is complete) 5) Read the RX register and return the value I believe this is different than the way you write bytes. Indeed! The first code that you posted is different than the last one: in the first code you wait for RDRF bit to be set, and in the last code you wait for TDRE. The first code: Code: U8 Spi_SendByte(U8 data_, eSpiCs spiCs_, bool assertCs_ ) { /* ... code omitted ... */ while( !(m_pRegs->SR & BIT0) ) { ; // wait for char } returnVal = (U8)m_pRegs->RDR; return( returnVal ); } The last code: Code: unsigned char mySPI_Write(unsigned char data) { /* ... code omitted ... */ // wait for reception while ((AT91C_BASE_SPI0->SPI_SR & AT91C_SPI_TDRE) == 0); // return the recieved data dummy = AT91C_BASE_SPI0->SPI_RDR; return dummy; } Does it make a big difference? or both are correct? :) |
|
| Author: | dfridley [ Fri Mar 19, 2010 4:05 pm ] |
| Post subject: | Re: SAM7X512's SPI peripheral gets disabled on write to SPI_TDR |
Yep sorry about that. Darn cut paste error... The first is correct. Second is Wrong! |
|
| Author: | mDor [ Fri Mar 19, 2010 4:15 pm ] |
| Post subject: | Re: SAM7X512's SPI peripheral gets disabled on write to SPI_TDR |
dfridley wrote: Yep sorry about that. Darn cut paste error... The first is correct. Second is Wrong! OK, thank you very much! |
|
| Author: | dfridley [ Fri Mar 19, 2010 4:46 pm ] |
| Post subject: | Re: SAM7X512's SPI peripheral gets disabled on write to SPI_TDR |
Quote: OK, thank you very much! No problem. I hope it solves your issue. BTW: if you use this to read data I would call this passing 0xFF to the write instead of my example of "rxData = mySPI_Write(0)". This way the data line stays high and you are just toggling the clock line. ex. Code: // Of couse you would have to set and clear the chip selects
// and possibly send some command to the device you are working // with before the collection example. unsigned char buffer[20]; unsigned char index; // Other code that sets up the device to send data... for( index=0; index < sizeof(buffer); index++ ) { buffer[index] = mySPI_Write( 0xFF ); } |
|
| Author: | mDor [ Sun Mar 21, 2010 9:17 pm ] |
| Post subject: | Re: SAM7X512's SPI peripheral gets disabled on write to SPI_TDR |
Hi dfridley, Well, I went today to work to try the code that you posted, so before writing any code - I read your code again to fully understand what is does. Then, I noticed the following line that sets SPI_MR: Code: m_pRegs->MR = MR_MSTR | MR_PS | MR_MODFDIS; The MR_MODFDIS field (mode fault detection) stood out. I remember that I didn't modified that field in my configurations. Add to that what is written in the data sheet and the event of a "SPI disable" is possible: Quote: When a mode fault is detected, the MODF bit in the SPI_SR is set until the SPI_SR is read and the SPI is automatically disabled until re-enabled by writing the SPIEN bit in the SPI_CR (Con- trol Register) at 1. By default, the Mode Fault detection circuitry is enabled. The user can disable Mode Fault detection by setting the MODFDIS bit in the SPI Mode Register (SPI_MR). After disabling the mode fault detection feature, I didn't encounter any problems. I hope that this is really the reason and that no more problems will occur in the future. I appreciate your kind help! |
|
| Page 1 of 1 | All times are UTC + 1 hour [ DST ] |
| Powered by phpBB © 2000, 2002, 2005, 2007 phpBB Group http://www.phpbb.com/ |
|



Forum