SD Card write -> swapped blocks

brushlesspower
Posts: 5
Joined: Mon Nov 27, 2023 10:34 am

SD Card write -> swapped blocks

Postby brushlesspower » Mon May 12, 2025 9:31 am

Hello,

i have a random Problem.

i'am writing an array of 512 bytes (or 1024, 2048) to a SD Card in SPI Mode.
Sometimes (randomly) 2 of these Blocks are swapped

You can see the swapped blocks in the picture

Image

Code: Select all

FILE* f = fopen(c, "a");                  // IDF FS
    if (f == NULL/*!file*/) {
      Serial.println("- failed to open file for appending");
      sd_error = true;
      write_error(ERRORSDCARD, "SD Card LOGFILE", "Could not open file");
      readtemp = readcounter; // Fehler: readtemp zurücksetzen
    }
    else {
      /*for (uint16_t i = 0; i < copycounter; i++) {        // Kopiere den RAM-Buffer in den Write Buffer
        write_buf[i] = RAM_Buf[readcounter];
        readcounter ++;                                   // zähle readcounter hoch
        if (readcounter == RAMSIZE) {                     // Buffer Overflow
          readcounter = 0;                                // Overflow ausgleichen
        }
        }*/
      int fileerror;
      fileerror = fwrite(write_buf, 1, copycounter, f);   // Schreibe Write Buffer auf SD Karte -> dauert etwas...
      yield();                                            // RTOS -> Zeit für andere Tasks
      //Serial.print("fileerror fwrite: ");
      //Serial.println(fileerror);
      if (fileerror != copycounter) {                     // normalerweise rückgabewert = copycounter
        // ToDo: readcounter zurückrechnen -> overflow beachten (readtemp verwenden)
        readtemp = readcounter; // Fehler: readtemp zurücksetzen
        ErrorCode = ErrorCode | ERRORSDCARD;
        write_warning(String(fileerror), "Log File Write error");
      }

      fileerror = fflush(f);                              // endgültig schreiben
      Filewritecounter++;                                 // Debugcounter -> ToDo: nur wenn erfolgreich?
      //Serial.print("fileerror fflush: ");
      //Serial.println(fileerror);
      if (fileerror != 0) {                               // normalerweise ok
        // ToDo: readcounter zurückrechnen -> overflow beachten (readtemp verwenden)
        readtemp = readcounter; // Fehler: readtemp zurücksetzen
        ErrorCode = ErrorCode | ERRORSDCARD;
        write_warning(String(fileerror), "Log File Flush error");
      }

      //file.close();                                   // Arduino FS
      fileerror = fclose(f);                            // IDF FS
      //Serial.print("fileerror fclose: ");
      //Serial.println(fileerror);
      if (fileerror != 0) {                             // normalerweise ok
        // ToDo: readcounter zurückrechnen -> overflow beachten (readtemp verwenden)
        readtemp = readcounter; // Fehler: readtemp zurücksetzen
        ErrorCode = ErrorCode | ERRORSDCARD;
        write_warning(String(fileerror), "Log File Close error");
      }
      readcounter = readtemp; // Wenn alles OK wird readcounter um 512Byters erhöht; Wenn fehler wbleibt readcounter so wie zuvor,
      fileerror = 0;
in my Errorlog is no SD Card Write error.
Is there an issue with ffwrite?

Sprite
Espressif staff
Espressif staff
Posts: 10169
Joined: Thu Nov 26, 2015 4:08 am

Re: SD Card write -> swapped blocks

Postby Sprite » Tue May 13, 2025 2:12 am

Where does write_buf come from?

brushlesspower
Posts: 5
Joined: Mon Nov 27, 2023 10:34 am

Re: SD Card write -> swapped blocks

Postby brushlesspower » Wed May 14, 2025 7:02 am

write_buf is the array which should be written to SD Card
RAM_Buf is filled by the second core with readable lines
everything uint16, that there cant be any overflow

Writecounter is the position where the RAM_Buf is filled
ReadCounter is the Position where it is saved to SD Card

Code: Select all

#define RAMSIZE 65536   //uint16 = 65535

volatile uint8_t RAM_Buf[RAMSIZE] = {}; // RAM Buffer für Loggen
uint8_t write_buf[2048] = {};           // Write Buffer für SD (maximal SD Sector Size = 512 Bytes?)
volatile uint16_t writecounter = 0;
volatile uint16_t readcounter = 0;
volatile uint16_t bufferload = 0;

Code: Select all

char printbuf[255] = {};
printsize = sprintf((char *)printbuf, "$%.1f;%.0f;%u;%.0f;%.1f;%.0f;%.0f;%u;%u;%u;%.1f\r\n", voltage_bat, current, rotations, temperatur_avg, voltage_12v, temperatur1, temperatur2, throttle_input, throttle_output, bufferload, voltage_bec );
for (uint8_t i = 0; i < printsize; i++) {
      RAM_Buf[writecounter] = printbuf[i];
      //Serial.write(RAM_Buf[writecounter]);
      writecounter++;   // uint16 -> überlauf bei 65536 = RAMSIZE
      /*if (writecounter == RAMSIZE) {
        writecounter = 0;
        }*/
    }

Code: Select all

uint16_t writetemp = writecounter;     // arbeitskopie
    uint16_t readtemp = readcounter;       // arbeitskopie
    uint16_t copycounter = 0;
    uint16_t diff = writetemp - readtemp;

       Overflow ausgleich nicht mehr nötig RAMSize = uint16 Size
      if ((writetemp - readtemp) < 0) {           // writecounter ist hinter Readcounter -> dazwischen liegt ein Bufferoverflow
      writetemp += RAMSIZE;                     // Overflow ausgleichen
      }*/

    if (diff > 2048) { // Differenz > 2kByte
      copycounter = 2048;                     // 2kB auf SD schreiben
    }
    else if (diff > 1024) { // Differenz > 1kByte
      copycounter = 1024;                     // 1kB auf SD schreiben
    }
    else if (diff > 512) {  // Differenz > 512Byte => Normalfall ToDo: 512 ersetzen durch SD sector Size
      copycounter = 512;                      // 512Bytes auf SD schreiben
    }
    else if (LOGSTARTED && !LOG) {            // Differenz kleiner als 512Bytes => Rest vom letzten Log
      copycounter = diff/*writetemp - readtemp*/;     // 1 bis 511 Bytes auf SD Karte schreiben
      LOGSTARTED = false;                     // es gibt keine Daten mehr zu schreiben
      
    }
    else {                                    // Sollte nicht sein
      return;                                 // nix machen
    }

    for (uint16_t i = 0; i < copycounter; i++) {        // Kopiere den RAM-Buffer in den Write Buffer
      write_buf[i] = RAM_Buf[readtemp];
      readtemp ++;                                   // zähle readcounter hoch überlauf bei 65536 = uint16
      /*if (readcounter == RAMSIZE) {                     // Buffer Overflow
        readcounter = 0;                                // Overflow ausgleichen
        }*/
    }

Sprite
Espressif staff
Espressif staff
Posts: 10169
Joined: Thu Nov 26, 2015 4:08 am

Re: SD Card write -> swapped blocks

Postby Sprite » Thu May 15, 2025 2:04 am

Are you making sure that there is absolutely 100% no way the second core can be writing to a buffer while the first one is reading from it? Asking because I don't see any inter-task communication methods used.

brushlesspower
Posts: 5
Joined: Mon Nov 27, 2023 10:34 am

Re: SD Card write -> swapped blocks

Postby brushlesspower » Thu May 15, 2025 7:23 am

I'am sure that there is no protection.

So you are thinking that the RAM_Buf[] is already corrupted by race condition of 2 cores?

i thought that it would be ok when using internal RAM (not PSRAM).

But would i cause a Block swap? How?
What would be the easiest way to block it?

brushlesspower
Posts: 5
Joined: Mon Nov 27, 2023 10:34 am

Re: SD Card write -> swapped blocks

Postby brushlesspower » Thu May 15, 2025 12:28 pm

i just try to read something about dual core variable access.

so everytime one of the cores is reading (or writing) to RAM_buf it have to

Code: Select all

xSemaphoreTake
and when finished

Code: Select all

xSemaphoreGive
and then it should be solved?

lbernstone
Posts: 953
Joined: Mon Jul 22, 2019 3:20 pm

Re: SD Card write -> swapped blocks

Postby lbernstone » Thu May 15, 2025 3:29 pm

We can't see all your code, so we can't guarantee what will fix it.
The more common way to pass data between tasks is with a queue, but a semaphore or mutex will work fine to force "single-file" as well.

MicroController
Posts: 2173
Joined: Mon Oct 17, 2022 7:38 pm
Location: Europe, Germany

Re: SD Card write -> swapped blocks

Postby MicroController » Fri May 16, 2025 2:19 pm

... but a semaphore or mutex will work fine ...
Assuming you have your ring buffer logic implemented correctly in the first place.

Who is online

Users browsing this forum: ChatGPT-User, PerplexityBot, trendictionbot and 1 guest