Las tarjetas SD de 8 GB sobrescriben datos de forma intermitente

He heredado un dispositivo integrado personalizado que utiliza un MCU Renesas H8S para almacenar datos en una tarjeta SD. Hace esto directamente sin que el sistema de archivos implemente SPI a bajo nivel en el firmware escrito en ensamblador. La versión original (MMC, pre SPI) funcionó durante muchos años sin problemas en tarjetas SD de 2 GB.

Hace un tiempo, comenzamos a instalar tarjetas Kingston de 8 GB en los dispositivos, ya que nuestra opción tradicional de tarjeta de 2 GB se estaba volviendo escasa, por lo que tuvimos que realizar algunas modificaciones en las rutinas de lectura/escritura del firmware para admitir los "nuevos" modos y tiempos SPI. El desarrollador hizo que esto funcionara y pensó que todo estaba bien.

Ahora hemos descubierto que algunos de nuestros datos se sobrescriben de forma intermitente con bloques de 0x55, es decir. como 55555555555555555... (esto es 85d, o curiosamente 101010101 en binario)

Durante el funcionamiento normal, nuestro firmware almacena en búfer algunos datos entrantes en la RAM y la NVRAM hasta que llega a un bloque 512, luego escribe un solo bloque en el SDC, realizando un seguimiento de la siguiente dirección. Sigue escribiendo en los bloques 512 subsiguientes, sin retroceder nunca mientras se agrega a un registro.

Sin embargo, cuando volvemos a leer los datos, a veces los datos más antiguos se han sobrescrito con 0x55 en fragmentos de algún tamaño de página actualmente desconocido (al menos 128 bytes, por lo que probablemente 512). Parece que estos 55 se escriben 'debajo' de nuestros datos y, a veces, se superponen.

Podemos depurar el firmware (con HEW y el emulador de hardware USB) pero no podemos ver un problema en nuestro código. Estoy un poco ciego a lo que está sucediendo en el SDC, ya que la única forma que conozco de verlo es eliminarlo y crear una imagen (usándolo dden mi Mac), que es muy lento. (De hecho, he tenido problemas para ver algo sensato en las imágenes SD de 8 GB).

Estoy bastante seguro de que los datos se corrompieron en la tarjeta durante la escritura o poco después, y no se volvieron a leer incorrectamente. Tenemos dos rutinas de lectura bastante diferentes y ambas devuelven los mismos datos. La depuración muestra que los datos 55 salen de la tarjeta y no se rompen más adelante en el procesamiento.

Por lo tanto, solo puedo suponer que se trata de una condición de carrera, algún almacenamiento en caché interno o almacenamiento en búfer en el SDC, o posiblemente el tamaño del bloque de borrado que podría estar sobrescribiendo áreas más grandes que los 512 bloques que estamos escribiendo.

La tarjeta se usa para otros fines en otras áreas reservadas más bajas, con varios ciclos de trabajo en nuestro firmware y también se interrumpe, por lo que es posible que haya un conflicto allí, pero pensé que esto habría sucedido hace años.

Solo pasa con estas tarjetas 8BG. Todavía no hemos podido probar ninguna otra variante/marca de tarjeta, pero planeamos hacerlo.

Entonces mis preguntas son:

  1. ¿Existen riesgos conocidos y problemas comunes como este escribir directamente en la tarjeta y evitar un FS? (como la alineación de bloques)
  2. ¿Las tarjetas más grandes almacenan datos en memoria intermedia o caché internamente?
  3. es 0x55 un valor de "relleno" conocido (a diferencia del borrado normal de 0xFF)
  4. ¿Alguien puede pensar en lo que está causando las sobrescrituras intermitentes?

ACTUALIZAR

Hoy probé tantas tarjetas como pude encontrar, y encontré que todas menos una fallaban de manera similar, pero a veces diferente.

Result  Card
FAIL    8GB Kingston Ultra microSDHC Class 4 (CO8G Taiwan), production.
PASS    4GB Verbatim microSD HC Class 4 - brand new
FAIL    16GB SanDisk Ultra microSDHC 80mb/s 533x - new
FAIL    8GB SanDisk microSDHC I Class 4 "BI Made in China", reused.
FAIL    8GB SanDisk ULTRA microSD HC I - UHS Class 1, production.
FAIL    4GB SanDisk microSDHC class4 - new.
FAIL*   ADATA 4GB microSDHC class4 - new.
PASS    Transcend 2GB microSD (SC?) 
ON ORDER    Transcend 8GB microSD   
PASS    Verbatim 8GB microSDHC Class 10 
PASS    Kingston 8GB microSDHC Class 10 / UHC 1
FAIL    Un-branded Taiwan 8GB SDHC Class 10

De estas tarjetas, la "peor" es la Sandisk de 16 Gb que parecía impredecible e incluso movió los datos después de escribirlos, ya que las vistas posteriores de los datos son diferentes. La tarjeta ADATA también era diferente, ya que necesitaba dos lecturas para leer los datos reales. En la primera lectura, los datos solo eran parcialmente visibles, pero en la segunda lectura parecían correctos.

La mayoría de ellos se comportaron de la misma manera, como se describió anteriormente, con bloques antiguos que parecían haber sido sobrescritos cuando las escrituras posteriores se mueven a bloques nuevos (probablemente). Algunas de ellas fallaron casi inmediatamente después de una o dos escrituras de bloque 512, mucho peor que nuestras tarjetas Kingston primarias de 8 GB.

Después de ver cómo los datos se mueven de manera extraña en estos discos, actualmente siento que los discos más grandes están "haciendo cosas inteligentes" con los datos (como almacenamiento en búfer o almacenamiento en caché) que solo estamos experimentando porque estamos escribiendo/leyendo directamente en direcciones de memoria en lugar de respetando la capa del sistema de archivos (incluso si no hay un error específico en nuestro código).

ACTUALIZAR2

He aclarado y simplificado el escenario del problema, mostrando que es más simple:

  • Tengo algunos registros A almacenados en un área de la tarjeta SD
  • Una rutina escribe algunos B-logs en otra área en un bloque 512
  • Cuando leo los registros A, parecen dañados
  • Cuando vuelvo a leer los registros A, se "arreglan" solos, en la tercera lectura y devuelven los datos verdaderos.
  • La mayoría de las tarjetas que he probado muestran 00000000 en los datos cuando están dañados.

A partir de esta lectura, ahora estoy pensando que la corrupción es falsa, los datos en la tarjeta están bien y los datos devueltos por nuestro lector de registro son quizás el "flujo de tokens ocupados" o DO todavía se mantiene bajo desde la escritura. proceso. Parece que el proceso de lectura de los registros A está terminando con la escritura anterior de los registros B.

Sin embargo, mirando el código, parece que se está respetando el protocolo, y la macro de escritura no sale hasta que DO sube, después de enviar los relojes en un bucle (es decir, no 8 o 32, ¡solo los que necesite!)

Esta es la asamblea.

La parte con la que he estado experimentando es el _MMCSPIWrite512ByteBlockFromer2Acceptbucle final, al que intenté agregar varias cosas:

  • Se agregaron ciclos de lectura y/o escritura en lugar de solo relojes en el ciclo
  • Agregué 8 relojes ficticios justo antes de bajar CS
  • Agregué 32 relojes ficticios justo antes de bajar CS

La _MMCSPISendByter0lmacro envía 8 relojes con los bits.

Al atravesarlo, parece estar haciendo aproximadamente el proceso correcto. Sin embargo, es difícil contar las iteraciones exactas en los diversos bucles (en el emulador HEW, un poco lento), ¡y cualquier registro de depuración es demasiado difícil en asm! Y también el paso parece afectar el tiempo y, a veces, se bloquea.

Esta macro _MMCSPIWrite512ByteBlockFromer2es la función principal para escribir un bloque 512 en la tarjeta SD, desde un búfer ram apuntado desde er2.

Revisé la especificación SPI simplificada y la parte del token de error/aceptación parece ser correcta.

Como dije antes, este algoritmo de escritura tiene muchos años de práctica exitosa en miles de dispositivos, en algunas tarjetas. Así que no espero que sea un error tan básico en el protocolo. Simplemente no puedo ver el problema que parece dejar el estado de lectura leyendo 000000, posiblemente alrededor de los primeros 512 bytes leídos (u ocasionalmente h'55s, que en realidad podrían provenir de otra parte de nuestra aplicación).

    _MMCSPIWrite512ByteBlockFromer2 
        bset    H_MMCSPIDI                   ; raise DI
        bclr    H_MMCSPInCS                  ; lower CS
        mov.b   #h'ff,r0l                               ;8 clock cycles Ncs
        bsr     _MMCSPISendByter0l
        mov.b   #C_MMCSPICMD24,r0l                      ;cmd24 is the write block command
        bclr    #7,r0l                                  ;b'01 mask
        bset    #6,r0l
        bsr     _MMCSPISendByter0l
        bsr     _MMCSPISendArguments                    ;send four arguments/address
        mov.b   #h'ff,r0l                               ;h'ff is the unused dummy crc
        bsr     _MMCSPISendByter0l
        bset    H_MMCSPIDI                              ;send hi data with clocks till response obtained

    _MMCSPIWrite512ByteBlockFromer2WaitForResponse
        btst    H_MMCSPIDO                              ;check for response, send clocks till response zero bit arrives
        beq     _MMCSPIWrite512ByteBlockFromer2Responded
        bsr     _MMCSPIClock
        bra     _MMCSPIWrite512ByteBlockFromer2WaitForResponse
    _MMCSPIWrite512ByteBlockFromer2Responded
        bsr     _MMCSPIReceive7Bitsr0l                  ;response should be h'00 else an error
        bne     _MMCSPIWrite512ByteBlockFromer2Error
        mov.b   #h'ff,r0l                               ;send Nwr
        bsr     _MMCSPISendByter0l
        mov.b   #h'fe,r0l                               ;send start of data header
        bsr     _MMCSPISendByter0l
    _MMCSPIWrite512ByteBlockFromer2DataStart
        mov.w   #d'512,e0                               ;send 512 byte block of data
    _MMCSPIWrite512ByteBlockFromer2DataLoop
        mov.b   @er2,r0l                                ;write data from er2 pointer, byte at a time, 512 times
        bsr _MMCSPISendByter0l
        inc.l   #1,er2
        dec.w   #1,e0
        bne     _MMCSPIWrite512ByteBlockFromer2DataLoop
    _MMCSPIWrite512ByteBlockFromer2CRC
        mov.b   #h'ff,r0l                               ;2 dummy crc bytes
        bsr _MMCSPISendByter0l
        mov.b   #h'ff,r0l
        bsr _MMCSPISendByter0l
     _MMCSPIWrite512ByteBlockFromer2CheckResponse
        rotl.b  r0l                                     ;get next lsb of response byte ready
        bsr     _MMCSPIClock
        bld     H_MMCSPIDO                              ;place mmc data output into response byte lsb
        bst     #0,r0l
        mov.b   r0l,r0h
        and.b   #b'00011111,r0h                         ;mask inappropriate bits of response and check
        cmp     #b'00001011,r0h                         ;check if a reject token?
        beq     _MMCSPIWrite512ByteBlockFromer2Reject
        cmp     #b'00000101,r0h                         ;check if an accept token?
        beq     _MMCSPIWrite512ByteBlockFromer2Accept
        bra     _MMCSPIWrite512ByteBlockFromer2CheckResponse    ;loop till reject or accept received

    _MMCSPIWrite512ByteBlockFromer2Accept
        bsr     _MMCSPIClock                            ;provide clocks for mmc writing clock requirements
        btst    H_MMCSPIDO
        beq     _MMCSPIWrite512ByteBlockFromer2Accept   ;keep clocking till write complete ack'd with a high DO

        bset    H_MMCSPInCS
        mov.b   #h'00,r0l
        rts
    _MMCSPIWrite512ByteBlockFromer2Error
    _MMCSPIWrite512ByteBlockFromer2Reject
        mov.b   #h'ff,r0l                               ;8 clock Nec
        bsr     _MMCSPISendByter0l
        bset    H_MMCSPInCS
        mov.b   #h'ff,r0l
        rts

Actualización 3

Hoy me concentré en dos cosas: la sugerencia del tamaño del bloque y el examen de la rutina READ, pensando más en el hecho de que los datos parecen estar escritos correctamente, pero se leen de forma intermitente.

En primer lugar, he intentado enviar CMD16 para establecer el tamaño del bloque en 512, pero

a) no tuvo ningún efecto en las fallas (si funcionó) b) No estoy 100% seguro de haberlo hecho bien en ensamblador, aunque durante la depuración obtuve un bit de "error de parámetro" establecido en la respuesta, que resolví y luego obtuve una respuesta 0x00, así que creo que estoy haciendo bien la llamada de comando... c) Al leer la especificación SPI simplificada, dice claramente que el tamaño del bloque es 512 para SDHC y este comando no se usa. Muchas personas dicen que debe establecer el tamaño del bloque: ¿esto es para tarjetas MMC? o la especificación es incorrecta?

En el caso de una tarjeta de memoria SD de capacidad estándar, este comando establece la longitud del bloque (en bytes) para todos los siguientes comandos de bloque (lectura, escritura, bloqueo). La longitud de bloque predeterminada se fija en 512 bytes. La longitud establecida es válida para los comandos de acceso a la memoria solo si se permite la operación de lectura de bloque parcial en CSD. En el caso de las tarjetas SDHC y SDXC, la longitud del bloque establecida por el comando CMD16 no afecta los comandos de lectura y escritura de la memoria. Siempre se utiliza una longitud de bloque fija de 512 bytes. Este comando es efectivo para el comando LOCK_UNLOCK. En ambos casos, si la longitud del bloque se establece en más de 512 bytes, la tarjeta establece el bit BLOCK_LEN_ERROR. En el modo DDR50, los datos se muestrean en ambos bordes del reloj.

En segundo lugar, examiné la rutina de lectura y, como antes, parece generalmente correcta. Anteriormente intenté agregar más "relojes ficticios" sin ningún efecto.

La rutina que lee desde SPI es la siguiente.

_MMCSPIRead512ByteBlockToer2
        bset    H_MMCSPIDI
        bclr    H_MMCSPInCS
        mov.b   #h'ff,r0l                               ;8 clock cycles Ncs
        bsr     _MMCSPISendByter0l
        mov.b   #C_MMCSPICMD17,r0l                      ;cmd17 is the read block command
        bclr    #7,r0l                                  ;b'01 mask
        bset    #6,r0l
        bsr     _MMCSPISendByter0l
        bsr     _MMCSPISendArguments                    ;send four arguments/address
        mov.b   #h'ff,r0l                               ;h'ff is the unused dummy crc
        bsr     _MMCSPISendByter0l
        bset    H_MMCSPIDI                              ;send hi data with clocks till response obtained
_MMCSPIRead512ByteBlockToer2WaitForResponse
        btst    H_MMCSPIDO                              ;check for response, send clocks till response zero bit arrives
        beq     _MMCSPIRead512ByteBlockToer2Responded
        bsr     _MMCSPIClock
        bra     _MMCSPIRead512ByteBlockToer2WaitForResponse     
_MMCSPIRead512ByteBlockToer2Responded
        bsr     _MMCSPIReceive7Bitsr0l                  ;response should be h'00 else an error
        bne     _MMCSPIRead512ByteBlockToer2Error
        bset    H_MMCSPIDI                              ;send hi data with clocks till response obtained
        bsr     _MMCSPIClock                            ;clock ensures previous zero on DO is removed
_MMCSPIRead512ByteBlockToer2WaitForData
        btst    H_MMCSPIDO                              ;check for response, send clocks till response zero bit arrives
        beq     _MMCSPIRead512ByteBlockToer2DataStart   ; because the response desired is h'fe , so last bit of this
        bsr     _MMCSPIClock                            ; response signals the start of data
        bra     _MMCSPIRead512ByteBlockToer2WaitForData     
_MMCSPIRead512ByteBlockToer2DataStart
        mov.w   #d'512,e0                               ;get 512 byte block of data
_MMCSPIRead512ByteBlockToer2DataLoop
        bsr     _MMCSPIReceiver0l                       ;get data and store into er2 pointer, 512 times
        mov.b   r0l,@er2
        inc.l   #1,er2
        dec.w   #1,e0
        bne     _MMCSPIRead512ByteBlockToer2DataLoop
_MMCSPIRead512ByteBlockToer2End
        bsr     _MMCSPIReceiver0l                       ;get 2 crc bytes
        bsr     _MMCSPIReceiver0l
        bsr     _MMCSPIReceiver0l                       ;a few more reads to endsure data is completely flushed
        bsr     _MMCSPIReceiver0l
        bsr     _MMCSPIReceiver0l                       
        bset    H_MMCSPInCS
        mov.b   #h'00,r0l
        rts
_MMCSPIRead512ByteBlockToer2Error
        mov.b   #h'ff,r0l                               ;8 clock Nec
        bsr     _MMCSPISendByter0l
        bset    H_MMCSPInCS
        mov.b   #h'ff,r0l
        rts

Actualización 4

Después de escribir algunas rutinas para escribir manualmente en los distintos registros, ahora puedo reproducir el problema de manera confiable con el conjunto de pasos más simple posible. También me familiaricé más con la depuración en HEW y la inspección de varias ubicaciones de memoria.

Resulta que la mayoría de los efectos que estaba viendo se debían a datos antiguos.

Después de escribir en los B-Logs, el comando SPI para leer de los A-Logs devuelve errores de la tarjeta. Sin embargo nuestro firmware no informa de este hecho, ni devuelve un resultado de error a las funciones superiores. Entonces, las funciones superiores de procesamiento de registros continúan leyendo los datos de un búfer de memoria que no ha sido modificado. El búfer tiene varios datos de registros anteriores de lectura/escritura y cuando se decodifica más (desde hexadecimal en binario hasta decimal en ascii) aparece todo tipo de datos falsos. Los 5555 en realidad provienen principalmente de 0xFF en RAM.

Entonces, aunque todavía no sé por qué el comando de lectura SPI está fallando, puedo cerrar esta pregunta, ya que se respondió en su mayoría y probablemente fue demasiado amplia.

Pruebe con otro tipo o marca de tarjetas. Su comportamiento varía, especialmente en modo SPI.
Sí, esperamos hacerlo mañana. Es relativamente fácil reproducir el comportamiento ahora que lo hemos encontrado, pero muy difícil de depurar. Probar variantes parece un camino más fácil, pero no parece que nos dará un verdadero cierre.
Verifique la fuente de alimentación para asegurarse de que el voltaje no baje durante las operaciones de escritura de la tarjeta (¡picos de corriente!). También puede probar y usar un analizador lógico para verificar los datos/comandos que realmente se transfieren a través de la línea a la tarjeta.
Buena idea @JimmyB Leí sobre la corriente adicional de algunas de estas tarjetas, pero por alguna razón no hizo clic para verificarlo. No tengo un analizador lógico, pero probablemente sea una buena inversión.
¿Qué tan seguro está de que la tarjeta de 4 GB realmente funciona? ¿Podría ser que pasó su prueba por casualidad y otra ejecución haría que fallara?
Uno no simplemente sobrescribe los datos en una SD. Primero tendría que borrar un bloque y luego escribirlo para reemplazar los datos aleatorios por una fila perfecta de 0x5... ¿Comprobó si su periférico spi está funcionando en la frecuencia correcta?
@pipe No estoy 100% seguro de que siempre funcionará a la perfección, pero funcionó sin problemas durante varias pruebas realizadas por dos personas, diferentes dispositivos, hasta miles de registros. Todas las demás fallaron dentro de los doscientos registros, a veces 20. Se "sentía" completamente diferente a las otras tarjetas que tenían problemas similares. Le daremos una prueba más dura una vez que haya hecho la evaluación de la variante de la tarjeta primero en amplitud, ¡antes de comprar miles de ellas!
@JHBonarius gracias. Verificaré la frecuencia. ¿Esto varía de una tarjeta a otra? Estos dispositivos han estado funcionando durante más de 10 años sin ningún problema antes.
@JHBonarius Reuní el borrado de bloque antes de que la tarjeta hiciera implícitamente la escritura, ¿cuándo le escribes?
Creo que lo que JH quiere decir es que la tarjeta nunca escribirá 0x55 en ninguna parte si no le dices explícitamente que escriba esos datos. No hay bloques "medio vacíos" que contengan algunos datos antiguos y nuevos.
Sin embargo, puede ser una buena idea borrar (CMD38) toda la tarjeta antes de cada nueva prueba. De lo contrario, es posible que le resulte difícil saber qué bloques se escribieron recientemente y cuáles son de ejecuciones de prueba anteriores.
Re el funcionamiento de la tarjeta de 4 GB: Supuestamente hay algunas tarjetas SD SC de 4 GB de tamaño. Esos están fuera del rango permitido oficialmente de máx. 2GB para SDSC pero técnicamente posible. Tal vez su tarjeta de 4 GB no sea una tarjeta SD HC sino (al menos técnicamente) una de esas SDSC de 4 GB no estándar.
Realmente sería de ayuda si pudiera capturar y compartir los rastros de señales reales con un analizador lógico.
"Cuando vuelvo a leer los registros A, se "arreglan" solos, en la tercera lectura" - ¿Podemos ver su rutina de lectura? Es posible que tenga algún tipo de problema de tiempo con algunas tarjetas.
@JimmyB He agregado la rutina de lectura. Empecé a sentir que aquí es donde radica el problema hoy... También estoy escribiendo algunas rutinas de prueba para leer/escribir/leer registros, pero es un trabajo terriblemente lento en ensamblador.
@JimmyB Lo sé, no tengo uno, así que he estado mirando varios productos/reseñas. Tengo un osciloscopio barato, así que podría intentar ver qué pueden capturar 2 canales. Probablemente valga la pena gastar unos cientos de dólares en un modelo de rango medio bajo, pensando en el ahorro de tiempo.
Considere un LA basado en PC (USB). Los decentes (8-16 canales, 100+MHz) se pueden encontrar por debajo de 100€. Piezas baratas con funcionalidad posiblemente suficiente por mucho menos.
Re CMD16: Como lees, las tarjetas SDHC solo admiten bloques de 512 bytes. Pero los SDSC pueden tener otros valores predeterminados. Por lo tanto, la forma más sencilla de admitir tarjetas HC y SC es establecer siempre el tamaño del bloque a través de CMD16, asegurándose de que cualquier tarjeta funcione en bloques de 512 bytes. Las tarjetas HC ignorarán el comando y mantendrán su 512 predeterminado, las tarjetas SC respetarán el comando y operarán en 512 después. De esta forma, no tiene que implementar la detección del tipo de tarjeta ni adaptarse a posibles tamaños de bloque diferentes de una tarjeta a otra.
Por cierto. Estoy seguro de que hay/había una buena razón para usar el lenguaje ensamblador. ¿Pero no puedes cambiar a un idioma de nivel superior? ¿Al menos para partes del código?
@JimmyB correcto: solo tenemos problemas con las tarjetas HC. Ninguno de los SDSC ha tenido nunca un problema. Así que descartaré el tamaño de bloque variable.
@JimmyB ¡Me encantaría! Ya lo investigaré en otro proyecto, pero el desarrollador anterior me advirtió sobre las dificultades. Estamos cerca de los límites de recursos del chip/placa, por lo que agregar la sobrecarga de un idioma puede ser fatal. También necesitaríamos escribir un código de pegamento para mapear todas las cosas de bajo nivel de bricolaje actuales como GPIO, red, memoria, interrupciones, etc. para que los dos idiomas puedan compartir "infraestructura" y decidir quién es el jefe.
Acabo de comprar uno de estos después de mucha deliberación: ikalogic.com/scanaquad-logic-analyzer-signal-generators

Respuestas (1)

¿Existen riesgos conocidos y problemas comunes como este escribir directamente en la tarjeta y evitar un FS? (como la alineación de bloques)

Generalmente no, usar la tarjeta como un simple dispositivo de bloqueo es perfectamente seguro. Sin embargo , sin una capa FS, usted es responsable de lo que hace. Entonces, si tiene una falla en su código, puede afectar más a la tarjeta SD.

¿Las tarjetas más grandes almacenan datos en memoria intermedia o caché internamente?

No. Escriben el bloque y una vez que ya no indican "ocupado" (R1b) los datos son persistentes. - Usted consulta el estado ocupado antes de operar más en la tarjeta, ¿no es así?

es 0x55 un valor de "relleno" conocido (a diferencia del borrado normal de 0xFF)

No. Los bloques borrados se leen como 0xFF o 0x00 (según la tarjeta).

¿Alguien puede pensar en lo que está causando las sobrescrituras intermitentes?

Me parece interesante que los datos corruptos sean consistentemente 0x55.

Pensando en ello, ¿sabe que las tarjetas SDSC (<= 2 GB) son direccionables por bytes , mientras que las tarjetas SDHC y SDXC son direccionables por bloques solamente? Por lo tanto, enviar la dirección 0x0200 a una tarjeta SDSC direccionará el comienzo del segundo bloque de 512 bytes, mientras que la misma dirección en una tarjeta SDHC/XC se referirá al comienzo del bloque 512 de la tarjeta, es decir, la dirección de byte 262144. Si si no verifica y respeta el tipo de tarjeta, es posible que se esté metiendo en problemas, o al menos no esté escribiendo datos donde cree que debería estar.

Además, asegúrese de esperar el final de cada operación realizada por la tarjeta.

Una tarjeta de 8 GB que probé no funcionó solo con los 8 relojes SPI "ficticios" estándar después de cada comando. Ahora, para estar seguro, siempre envío 32 relojes (4 bytes) y todo funciona a la perfección.

Es posible que desee verificar el tamaño de bloque de sus tarjetas o, para estar seguro, establecerlo en 512 bytes (CMD16) para todas las tarjetas durante la inicialización. De lo contrario, la tarjeta puede estar esperando más datos mientras cree que ya envió suficientes y le sucederán cosas malas a sus datos.

Me has dado muchas ideas y cosas para explorar aquí. ¡Desde una breve inspección del código, creo que no estamos respetando el direccionamiento de bloques! Entonces, los datos estarían dispersos en cada bloque 512.
El reloj ficticio parece que podría ser muy relevante, vea mi actualización sobre cómo luego se lee "empujar" los datos en una de las tarjetas (Sandisk de 16 Gb). Al verificar el código, lee 2 bytes crc y luego 3 lecturas ficticias "para garantizar que los datos se vacíen", por lo que puedo probar fácilmente agregando al menos 5 más a eso, o 29 más como sugiere.
Ignore eso sobre los bytes de CRC, estaba mirando la función incorrecta y ahora he leído correctamente 1 byte = 8 relojes.
He vuelto a intentar agregar más lecturas/escrituras y relojes, consulte update2 en la descripción. He estado ocupado en otros proyectos, pero ahora estoy de nuevo en esto y he simplificado el problema.
¿Están configurados correctamente los parámetros antes de llamar a _MMCSPISendArguments para la dirección de escritura?
sí, estoy bastante seguro de que lo son: están salpicados alrededor del código de llamada, por lo que no los he verificado todos, pero parecen estar configurando las direcciones correctas para que se escriban los datos de origen (desde un búfer de RAM copiado de normalmente NVRAM). Esto siempre ha funcionado (durante casi 10 años), por lo que, presumiblemente, si esos argumentos estuvieran equivocados, nos habríamos dado cuenta.
Otro pensamiento: algunas fuentes recomiendan aplicar resistencias pull-up a los pines de la tarjeta que no se usan en el modo SPI.
Estoy marcando esto como correcto, ya que me ha llevado correctamente a estar más cerca del problema subyacente, y mi pregunta original es principalmente una pista falsa debido al almacenamiento en búfer complejo en NVRAM y RAM: en su mayoría estaba viendo datos antiguos cuando la tarjeta falló para responder a un comando de lectura. Ahora tengo más preguntas que plantearé por separado y trataré de mantener su alcance más pequeño.
En realidad, ¡esto contiene la respuesta! Acabo de implementar la sugerencia de direccionamiento de bloques y solucionó el problema por completo. Estaba dudando sobre esto porque parecía difícil, pero en realidad fue un cambio bastante fácil y aislado en las dos macros de lectura/escritura. Sé que no tengo permitido decirlo pero - ¡Gracias Jimmy!
Genial que finalmente pudiste hacerlo funcionar, y que yo pude ayudar un poco. :)
Ayudaste mucho. Leí el problema de abordar el bloque en el sitio de elm-chan de pasada, pero fue su nota la que despertó la conciencia para actuar.