3
\$\begingroup\$

I'm using STM32F051R8T6 on STM32F0-Discovery board and M25P80 flash IC as a peripheral. I've got the SPI configured in 3-line (+ 1 software chip select line), full-duplex mode and I'm using polling to send and receive data. I've had a lot of issues trying to get SPI to work properly and what it all boiled down to is this series of tests executed consecutively, which exposed a bug I can't explain.

Step 1: Request a signature from the peripheral. Signature is 0x13, works 99% of the time.

Step 2: Enable writing to the peripheral by issuing a command to it.

Step 3: Read the contents of the status register to make sure that previous command made it through.

Since the peripheral operates in full-duplex mode, but data is never sent in both directions at the same time, input buffer needs to have the same dimensions as the output buffer and data written to it has some offset.

Problem arises at the last step. If only a single byte is requested to be read, SPI doesn't register data being transmitted to it! If more bytes are requested (by using 1 + X as the fourth function parameter), contents of the input buffer are following:

@ 1 + 3:
0x00 0x00 0x02 0x13

@ 1 + 4:
0x00 0x00 0x02 0x13 0x00

@ 1 + 5:
0x00 0x00 0x02 0x13 0x02 0x00

@ 1 + 6:
0x00 0x00 0x02 0x13 0x02 0x00 0x02

@ 1 + 7:
0x00 0x00 0x02 0x13 0x02 0x00 0x02 0x02

And here are the signals on MISO line (red) and SCK (yellow) when reading 1 + 3 bytes in total. First packet is the second step. As expected, nothing is transmitted by the flash IC. Second packet is step no. 3. Peripheral is silent during the first byte and then outputs 0x02 on all following bytes, but only the third bytes is read successfully. And then it fails to read bytes 4 and 6 (all further bytes are read correctly).

enter image description here

So I don't know how to explain the fact that data being read doesn't fully correspond to what I'm seeing on the oscilloscope. An the weirdest thing is - there's a 0x13 byte being read during the third step! If first step is removed, this byte is being read as 0x00. Seems like a memory corruption on STM32 side, but I can't find a reason for it since buffers are sanitized with memset() and SPI's internal buffer is flushed with HAL_SPI_FlushRxFifo(&hspi1).

Here's the code:

uint8_t command;
uint8_t inBuffer[16];
uint8_t outBuffer[16];

/* Step 1: Check if IC is alive */
{
  memset(inBuffer, 0x00, 16);
  memset(outBuffer, 0x00, 16);
  command = OPCODE_RES;
  outBuffer[0] = command;

  spi_select(M25P80);
  HAL_SPI_TransmitReceive(&hspi1, outBuffer, inBuffer, (1 + 3) + 1, TIMEOUT);
  spi_deselect(M25P80);

  HAL_SPI_FlushRxFifo(&hspi1);
}

for (volatile uint32_t i = 0; i < 200; i++);

/* Step 2: Enable writing */
{
  memset(inBuffer, 0x00, 16);
  memset(outBuffer, 0x00, 16);
  command = OPCODE_WREN;
  outBuffer[0] = command;

  spi_select(M25P80);
  HAL_SPI_Transmit(&hspi1, outBuffer, 1, TIMEOUT);
  spi_deselect(M25P80);

  HAL_SPI_FlushRxFifo(&hspi1);
}

for (volatile uint32_t i = 0; i < 200; i++);

/* Step 3: Make sure writing is enabled */
{
  memset(inBuffer, 0x00, 16);
  memset(outBuffer, 0x00, 16);
  command = OPCODE_RDSR;
  outBuffer[0] = command;

  spi_select(M25P80);
  HAL_SPI_TransmitReceive(&hspi1, outBuffer, inBuffer, 1 + 1, TIMEOUT);
  spi_deselect(M25P80);

  HAL_SPI_FlushRxFifo(&hspi1);
}

Some details about STM's SPI API. In a call like:

HAL_SPI_TransmitReceive(&hspi1, outBuffer, inBuffer, (1 + 3) + 1, TIMEOUT);

&hspi1 - peripheral handle  containing its settings and configurations
outBuffer - pointer to a memory location with data to be sent
inBuffer - pointer to a memory location to which received data is written
(1 + 3) + 1 - number of bytes to receive/transmit; 
              in this case 1 - command byte, 3 - dummy bytes, 1 - received signature byte
TIMEOUT - a value in the range of 10000 which indicates at which point transmission must be 
aborted if failed to finish

EDIT 1: SPI Initialisation code as generated by STM32CubeMx:

hspi1.Instance = SPI1;
hspi1.Init.Mode = SPI_MODE_MASTER;
hspi1.Init.Direction = SPI_DIRECTION_2LINES;
hspi1.Init.DataSize = SPI_DATASIZE_8BIT;
hspi1.Init.CLKPolarity = SPI_POLARITY_LOW;
hspi1.Init.CLKPhase = SPI_PHASE_1EDGE;
hspi1.Init.NSS = SPI_NSS_SOFT;
hspi1.Init.BaudRatePrescaler = SPI_BAUDRATEPRESCALER_256;
hspi1.Init.FirstBit = SPI_FIRSTBIT_MSB;
hspi1.Init.TIMode = SPI_TIMODE_DISABLED;
hspi1.Init.CRCCalculation = SPI_CRCCALCULATION_DISABLED;
hspi1.Init.NSSPMode = SPI_NSS_PULSE_DISABLED;
HAL_SPI_Init(&hspi1);

EDIT 2: Now that I've lowered the speed down to 488 b/s, everything works fine. I'll stick with this hack for a while.

\$\endgroup\$
5
  • 1
    \$\begingroup\$ Your bus looks pretty noisy. For troubleshooting, I would slow down the bus speed to a crawl and see if the results become more consistant. Also, often the Read-Enable command only works on the very next instruction. I'm not sure about this specific memory, but you may be missing your opportunity by reading the Status register. \$\endgroup\$
    – bitsmack
    Feb 11, 2015 at 16:57
  • \$\begingroup\$ What are your Clock Polarity and Phase settings? Can you show us your SPI initialization code? \$\endgroup\$ Feb 11, 2015 at 17:03
  • \$\begingroup\$ @bitsmack the speed is already at 250kHz, which is as low as I can go without disturbing other peripherals' clocks. I guess I really could go lower for testing purposes. Regarding missing an opportunity to read the register - this has absolutely nothing to do with it as I can see the data being sent on the oscilloscope, the problem is that the STM32 doesn't read it correctly for some reason. \$\endgroup\$
    – andrey
    Feb 12, 2015 at 8:39
  • \$\begingroup\$ @BruceAbbott I've updated the OP with SPI initialisation code. \$\endgroup\$
    – andrey
    Feb 12, 2015 at 8:40
  • \$\begingroup\$ a) You don't actually show 0x13 in your waveform. You only show command + three dummy bytes, signature is missing; b) Bus is HiZ during dummy bytes, activate pull-up on MISO; c) Check your pin setup and multiplexing; d) M25P80 is supposed to clock data out on falling edge, your waveform shows transitions at rising edge, make of that what you will; e) You're supposed to wait 30us after RES to transition into standby mode (cf. datasheet) before releasing #CS. f) Do a READ IDENTIFICATION, not WREN and compare those data to the expected values. \$\endgroup\$
    – FRob
    May 19, 2015 at 22:36

3 Answers 3

1
\$\begingroup\$

In my opinion you should use the Standard Peripheral Library for STM because is more stable and does not hide you the low level stuff which gives you the more controlable environment.Also you need to be careful at the CS pin timing, many devices require to hold that value a while until you start the transmission.

\$\endgroup\$
0
\$\begingroup\$

Commands, addresses, or input data are latched on the rising edge of the clock input, while output data is shifted out on the falling edge of the clock input.

\$\endgroup\$
7
  • \$\begingroup\$ Not with SPI, no. See RM0091 for reference. \$\endgroup\$
    – andrey
    Mar 19, 2015 at 4:53
  • 1
    \$\begingroup\$ Sorry , the M25P80 data sheet says that. Does that mean you have to read on 1EDGE, and write on 2EDGE? \$\endgroup\$
    – sam
    Mar 21, 2015 at 21:16
  • \$\begingroup\$ Clock: The C input signal provides the timing of the serial interface. Commands, addresses, or data present at serial data input (DQ0) is latched on the rising edge of the serial clock (C). Data on DQ1 changes after the falling edge of C. \$\endgroup\$
    – sam
    Mar 21, 2015 at 21:17
  • \$\begingroup\$ Ah, sorry, I misread your original message. Yes, M25P80 does shift data out at falling edge. What is your point though? \$\endgroup\$
    – andrey
    Mar 23, 2015 at 7:39
  • \$\begingroup\$ If STM32 is reading at rising edge and data doesn't start until falling edge, how do you read an address? Read 2 bytes and <4?? \$\endgroup\$
    – sam
    Mar 23, 2015 at 23:28
0
\$\begingroup\$

Try

hspi1.Init.CLKPhase = SPI_PHASE_2EDGE;

The datasheet for your device says "and output data is available from the falling edge of C." I believe this is where you would be setting that.

\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

Not the answer you're looking for? Browse other questions tagged or ask your own question.