Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

STM32: SPI.readinto ignores write_value, causes SD cards not to detect #3176

Closed
jerryneedell opened this issue Jul 20, 2020 · 11 comments · Fixed by #3431
Closed

STM32: SPI.readinto ignores write_value, causes SD cards not to detect #3176

jerryneedell opened this issue Jul 20, 2020 · 11 comments · Fixed by #3431
Assignees
Milestone

Comments

@jerryneedell
Copy link
Collaborator

related to
adafruit/Adafruit_CircuitPython_SD#36

I know the "on-board" SDcard is not support on the feather STM32F405.
I tried attaching an Adalloger Featherwing using sdcardio and it is not able to detect the card
The also happens with the Adafruit_CircuitPython_SD library
see
adafruit/Adafruit_CircuitPython_SD#36

Press any key to enter the REPL. Use CTRL-D to reload.
Adafruit CircuitPython 6.0.0-alpha.1-111-g8f928340c on 2020-07-20; Feather STM32F405 Express with STM32F405RG
>>> import sdcardio_lib
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "sdcardio_lib.py", line 9, in <module>
OSError: no SD card
>>> 

code -- sdcardio_lib.py

import busio
import sdcardio
import board
import storage
import sys
import os

# Connect to the card and mount the filesystem.
sdcard = sdcardio.SDCard(board.SPI(), board.D10)
vfs = storage.VfsFat(sdcard)
storage.mount(vfs, "/sd")
sys.path.append("/sd")
sys.path.append("/sd/lib")
@hierophect hierophect added the stm label Jul 20, 2020
@hierophect hierophect changed the title No SD Card detected by sdcardio on STM32F405 -- with adalogger STM32: No SD Card detected by sdcardio on STM32F405 -- with adalogger Jul 20, 2020
@tannewt tannewt added this to the 6.0.0 milestone Jul 20, 2020
@jepler
Copy link
Member

jepler commented Jul 22, 2020

Good session, nRF52840 (particle xenon):
image

Bad session, STM32F405 feather
image

Notice how outside the "40 00 00 00 95" packet sent to initialize the SD card into SPI mode, the MOSI pin stays high on nRF but doesn't on STM. I believe this is because the write_value (a static 8 bit value to be output while the read is ongoing) is ignored in the stm port:

bool common_hal_busio_spi_read(busio_spi_obj_t *self,
        uint8_t *data, size_t len, uint8_t write_value) {
    if (self->miso == NULL) {
        mp_raise_ValueError(translate("No MISO Pin"));
    }
    HAL_StatusTypeDef result = HAL_SPI_Receive (&self->handle, data, (uint16_t)len, HAL_MAX_DELAY);
    return result == HAL_OK;
}

I suspect the underlying cause is the same here and in adafruit/Adafruit_CircuitPython_SD#36 and will be fixed by the same fix -- whatever that is.

@jepler jepler changed the title STM32: No SD Card detected by sdcardio on STM32F405 -- with adalogger STM32: SPI.readinto ignores write_value, causes SD cards not to detect Jul 22, 2020
@jepler
Copy link
Member

jepler commented Jul 22, 2020

I've taken the liberty of punting this to @hierophect but can take it back if that's preferred.

@hierophect
Copy link
Collaborator

I can check it out, I'll add it to my list.

@jepler
Copy link
Member

jepler commented Jul 22, 2020

Changing the readinto code like this works, but may not be acceptable (it puts a potentially large variable size array on the stack):

 bool common_hal_busio_spi_read(busio_spi_obj_t *self,
-        uint8_t *data, size_t len, uint8_t write_value) {
+        uint8_t *data_in, size_t len, uint8_t write_value) {
     if (self->miso == NULL) {
         mp_raise_ValueError(translate("No MISO Pin"));
     }
-    HAL_StatusTypeDef result = HAL_SPI_Receive (&self->handle, data, (uint16_t)len, HAL_MAX_DELAY);
+    uint8_t data_out[len];
+    memset(data_out, write_value, len);
+    HAL_StatusTypeDef result = HAL_SPI_TransmitReceive (&self->handle,
+        data_out, data_in, (uint16_t)len,HAL_MAX_DELAY);
     return result == HAL_OK;
 }

@hierophect hierophect added the bug label Jul 24, 2020
@hierophect
Copy link
Collaborator

@jepler I'm fixing this today. Do you think you could provide me with a test sketch I can use to verify the fix? Something with a clear differentiation between the desired/undesired behavior. I can use my logic analyzer to view the same waveforms you did if you give me that same sketch. If it's just the same one jerryn provided let me know.

@hierophect
Copy link
Collaborator

hierophect commented Aug 25, 2020

I'd like to understand the significance of write_value a little better - most of the ports ignore it (including NRF52, which was in your example), and the ones that don't pass it directly into their SPI HALs. Is there something that STM32 does here that's nonstandard?

@hierophect
Copy link
Collaborator

Looks like this is a limitation of the ST HAL in regards to handling the MOSI line when using an SD card.
https://community.st.com/s/question/0D50X00009XkdpZ/sd-card-using-spi-initialization-stm32f303-issue

I'll look around for other workarounds since this one seems kind of slow. Micropython, STM32Duino, and even the ST FatFS library must all deal with this issue somehow.

@jepler
Copy link
Member

jepler commented Sep 11, 2020

While SPI just needs the value 0xff (all bits same), the nRF24 library needs specific values:

    def _reg_read(self, reg):
        buf = bytearray(2)  # 2 = 1 status byte + 1 byte of returned content
        with self._spi as spi:
            time.sleep(0.005)  # time for CSN to settle
            spi.readinto(buf, write_value=reg)
        self._status = buf[0]  # save status byte
        return buf[1]  # drop status byte and return the rest

@2bndy5
Copy link

2bndy5 commented Sep 12, 2020

@jepler @hierophect to be clear, the nRF24L01 hardware actually ignores MOSI after the first byte. I wrote that code when I was just starting out with CircuitPython in an effort to better familiarize myself with SPI transactions. Using spi.write_readinto() works just as well in regards to the nRF24L01 transceiver.

EDIT: Oops, I didn't mean to steal credit from Damien George, Peter Hinch, or Rhys Thomas. Turns out the use of spi.readinto() is from the original micropython source that was re-written for circuitpython.

@hierophect
Copy link
Collaborator

@jepler @tannewt Report from looking at this this morning. Delay obviously changes a lot based on the size of the incoming data. Measuring the duration of the memset with the DWT at 168MHz, these are the timings I got:

  • 3 byte flash setup read: 886ns delay vs 91µs read time (~1%)
  • 512 byte flash read: 73.6µs delay vs 3.2ms (~2.3%)

This is of course not variable with the speed of the SPI line, so it'll be a more significant delay compared to the actual transaction the faster the SPI is going. Is this a big enough deal for us to put in the API modification that Jeff suggested to allow write_value to be set as an "invalid" token, so that the memset can be skipped?

@hierophect
Copy link
Collaborator

Actual DWT Values:

  • Flash init of length 3:
    • Start = 3, memset = 152, transaction = 15426
    • Percent: (152-3)/(15426-152) = 0.97%, ~1%
  • Flash read of length 512
    • Start = 3, memset = 12368, end = 543700
    • Percent: (12368 - 3)/(543700 - 12368) = 2.32%

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging a pull request may close this issue.

5 participants