SSP/SPI problem LPC1227

取消
显示结果 
显示  仅  | 搜索替代 
您的意思是: 

SSP/SPI problem LPC1227

3,304 次查看
lpcware
NXP Employee
NXP Employee
Content originally posted in LPCWare by maxve2011 on Wed Jan 18 09:38:59 MST 2012
Hello,

I got stuck into a SSP/SPI problem with the LPC1227/301 arm-cortex m0.
I'am interfacing the lpc1227 with a external SPI flash memory.
I use the GPIO0_15 (ssel) pin as a digital output pin for chip selecting the flash memory.
Every time when the first command must be transmitted the LPC_SSP->DR register receives the value 0xff and not the command! Then when transmitting the next data all went fine!
Have I made something wrong? Any help I will appreciate.
Remark: There is no SSP/SPI code example for the lpc1227, or is there a good one? Please can someone give me some hints?
I use the following code:

void SPI_MasterInit(void)
{
register uint8_t i, Dummy=Dummy;
/* set up SPI port pins */
LPC_IOCON->PIO0_14 &= ~(0x7);
LPC_IOCON->PIO0_14 |= MODE_SPI; /*SCK*/
LPC_IOCON->PIO0_16 &= ~(0x7);
LPC_IOCON->PIO0_16 |= MODE_SPI; /*MISO*/
LPC_IOCON->PIO0_17 &= ~(0x7);
LPC_IOCON->PIO0_17 |= MODE_SPI; /*MOSI*/
/* Enable SSP0 peripheral clock */
LPC_SYSCON->SSPCLKDIV = 0x01;
/* Set DSS data to 8-bit, Frame format SPI,
CPOL = 0, CPHA = 0, and SCR is 1
*/
LPC_SSP->CR0 = (SSPCR0_DSS)|(SSPCR0_CPOL)|(SSPCR0_CPHA)|(SSPCR0_SCR);
/* Enable SSP controller */
LPC_SSP->CR1 = SSPCR1_SSE;
/* SSPCPSR clock prescale register, master mode,
minimum divisor is 0x02 for master mode
*/
/* Set bitfrequency to 3 MHz */
LPC_SSP->CPSR = 0x4;
for ( ;i < FIFOSIZE;i++)
{
Dummy = LPC_SSP->DR; /* clear the RxFIFO */
}
/* Enable the SSP/SPI Interrupt */
NVIC_EnableIRQ(SSP_IRQn);
/* Device select as master, SSP/SPI Enabled */
LPC_SSP->CR1 = SSPCR1_SSE;
/* Set SSPINMS registers to enable interrupts
* enable all error related interrupts */
LPC_SSP->IMSC = SSPIMSC_RORIM | SSPIMSC_RTIM;
return;
}

void SPI_MasterTransmit(uint8_t *buff, uint32_t Length)
{
register uint32_t i = 0;
register uint8_t Dummy = Dummy;
/* Validate parameters */
REQUIRE(buff != NULL && Length < UINT32_MAX);
for( ;i < Length;i++)
{
/* Move on only if NOT busy (=0) and TX FIFO not full (=1) */
while((LPC_SSP->SR & (SSPSR_BSY|SSPSR_TNF)) != SSPSR_TNF);
/* Put data/instruction into SSP data buffer and right justify */
LPC_SSP->DR = *buff;
buff++;
/* Wait until the Busy bit for transmission is cleared. */
while ((LPC_SSP->SR & (SSPSR_BSY|SSPSR_RNE)) != SSPSR_RNE);
Dummy = LPC_SSP->DR;
}
return;
}


void SPI_MasterReceive(uint8_t *buff, uint32_t Length)
{
register uint32_t i = 0;
/* Validate parameters */
REQUIRE(buff != NULL && Length < UINT32_MAX);
for( ;i < Length;i++)
{
/* As long as receive FIFO is not empty (=1), receiving data is always
* possible */
/* Wait with receiving data until the Busy bit is cleared */
while((LPC_SSP->SR & (SSPSR_BSY|SSPSR_RNE)) != SSPSR_RNE);
*buff = LPC_SSP->DR;
buff++;
}
return;
}

uint8_t src_addr[1] = {0};
uint8_t dest_addr[3] = {0};
uint8_t locbuffer[3] = {0};
#define WREN 0x06

a25lq_errCode A25LQ032_WREN(void)
{
/* CS active low */
SPI_SSEL_LOW;
/* SPI transmits WRITE_ENABLE instruction */
src_addr[0] = WRITE_ENABLE;
SPI_MasterTransmit(&src_addr[0],1);
/* CS active high */
SPI_SSEL_HIGH;
if(SPI_SSEL_HIGH)
{
return A25CMD_SUCCESS;
}
else return A25CMD_FAILURE;
}

When calling above function with SPI_MasterTransmit the SSP dataregister
receives the value 0xff and not 0x06!
Do I have to call SPI_MasterReceive() separately?

MV
0 项奖励
回复
4 回复数

2,357 次查看
lpcware
NXP Employee
NXP Employee
Content originally posted in LPCWare by fjrg76 on Thu Jan 24 21:31:30 MST 2013
Normal SPI transactions are two bytes length. When writing to the slave device the first byte is the command|address, while the second one is the data to be written. And when reading from the slave device, again, the first byte is the command|address, whilst the second byte is a dummy one.

For example, you want to read something from the SPI eeprom:

uint8_t EEPROM_Read(uint_8 Address)
{
uint8_t regVal;

SPI_Write(Address|Read); /* we don't care the first byte read onto the MISO line */
regVal = SPI_Write(DUMMY); /* Dummy byte is any value, and now we care about the data read over the MISO line */

return regVal;
}


When the first byte is being sent out over the MOSI line, the MISO line can be either high or low (depending on the pull-up or pull-down resistor attached to the pin), that's way you read 0xFF ('cause you're using a pull-up resistor). This first reading  byte is meaningless and not important, so you can discard it. But when the second byte is being sent out over the MOSI line by the master, perhaps the slave device is writting something back to the master, so this second byte that is read over the MISO line can be important and you should take it into account.
0 项奖励
回复

2,357 次查看
lpcware
NXP Employee
NXP Employee
Content originally posted in LPCWare by Rob65 on Sat Jan 21 01:42:58 MST 2012
I'm not sure about your level of understanding of SPI, so forgive me if I sound a bit like a teacher repeating something you already know

SPI is a synchronous bidirectional protocol with 4 signals (clock, data in, data out and enable).

There is only 1 clock line so every clock cycle one bit is written by the master and one bit is read by the master. If you now connect the MOSI and the MISO pin of the lpc1xxx (being SPI master) together, then when you write a bit you read a bit. So writing to the LPC_SSP->DR register, waiting for the SSP to finish transmitting the word and then reading LPC_SSP->DR will return you the same value.

If you now connect the A25LQ032 SPI Flash then the data you read from the DR register after a write depends on what the Flash does.

You surely have the datasheet (here's the PDF on Amic's website).
Have a look at figure 8 (page 19) . There you see what happens during one transaction:

1) During the first 8 clock cycles (1 byte) the command is transmitted towards the Flash
2) the next 24 cycles (3 bytes) contain the 24 bits address
*) Note that the data out line of the flash has a high impedance during these cycles meaning that you will just read 0xFF for all 4 bytes sent.
3) now you send one byte to the Flash, data content does not matter since this is just needed to generate the clock cycles.
4) when completed, read the DR register to return the data read during these cycles.

This is mostly how SPI works: you first send a command, the slave processes the command, and then you start sending dummy bytes to receive the data.

The Write Enable instruction is shown Figure 4 at page 15. Note how the data out line is always high impedance during the transaction and there is no response after sending the command.
So it is perfectly correct to see that you receive 0xFF when sending 0x06 with the Flash connected to the SPI bus.

To check for a working SPI, without having to bother with the protocol of the Flash, you can disconnect the Flash and connect the MISO and MOSI lines together - now you will read the same content as what you sent.
If you connect the MISO line to a constant high or low level you will receive 0xff or 0x00.
If you want to be completely sure about what happens you need a scope or logic analyzer.

Now some notes on your code:

01 void SPI_MasterTransmit(uint8_t *buff, uint32_t Length)
02 {
03     register uint32_t i = 0;
04     register uint8_t Dummy = Dummy;
05     /* Validate parameters */
06     REQUIRE(buff != NULL && Length < UINT32_MAX);
07     for( ;i < Length;i++)
08     {
09         /* Move on only if NOT busy (=0) and TX FIFO not full (=1) */
10         while((LPC_SSP->SR & (SSPSR_BSY|SSPSR_TNF)) != SSPSR_TNF);
11         /* Put data/instruction into SSP data buffer and right justify */
12         LPC_SSP->DR = *buff;
13         buff++;
14         /* Wait until the Busy bit for transmission is cleared. */
15         while ((LPC_SSP->SR & (SSPSR_BSY|SSPSR_RNE)) != SSPSR_RNE);
16         Dummy = LPC_SSP->DR;
17     }
18     return;
19 }
On lines 3 and 4 you are defining two variables as [I][B]register[/B][/I]. I think it is better to let the compiler do the optimizations unless you need a variable to be in a register because of speed (which is not the case here).

On line 7 there is no start condition for the for loop. Instead of setting i to 0 in the declaration it is best to do that over here; "for( i=0; i<Length; i++). One thing is readability (you immediately see that the loop variable is correctly initialized), the next thing is that as soon as you copy/paste a working section of code to another place you don't miss out the initialization.

On line 16 to are reading from the DR register into a local variable that is not being used in this function. This might be optimized away as soon as you use any compiler optimizing options (-O1/2/3/S).
If you want to be sure that this read action is not optimized away, make Dummy a volatile (i.e. volatile uint8_t Dummy).

Regards,

Rob

P.s: if you place C-code in your post, place [ CODE ] and [ /CODE ] tags around it to make it more readable.
0 项奖励
回复

2,357 次查看
lpcware
NXP Employee
NXP Employee
Content originally posted in LPCWare by maxve2011 on Fri Jan 20 11:34:35 MST 2012
Hi Rob,

The problem I encounter is the very first time when a command is transmitted by the SPI master, the SSP data register reads 0xff instead of the *buffer content which is 0x06.
The MOSI and MISO lines of the lpc1227 are connected with the DIN (data in) en DOUT (data out) lines of the flash memory.
What can I do to solve this problem???

MV
0 项奖励
回复

2,357 次查看
lpcware
NXP Employee
NXP Employee
Content originally posted in LPCWare by Rob65 on Wed Jan 18 11:18:27 MST 2012

Quote: maxve2011

Every time when the first command must be transmitted the LPC_SSP->DR register receives the value 0xff and not the command! Then when transmitting the next data all went fine!



The SSP->DR register can look funny.
If you [B]write [/B]to this register, the data is being used to generate an SSP transaction with the data being sent out over the MOSI line (SSP in master mode).
When the controller generates the clock pulses, it reads the MISO line and (when the required number of clock pulses have been generated) stores the data read in the FIFO that is attached to the [B]read [/B]part of the SSP->DR register.

So if you write to SSP->DR and then immediately read from it, you do not get the data you just wrote but the data that was received during the [B]previous[/B] clock cycles.

Hope this answers your question.

Regards,[INDENT]Rob
[/INDENT]
0 项奖励
回复