Lange Speicherzeiten für Bilder über SD-Kartenmodul

Hallo,

ich arbeite derzeit an einem Projekt, bei dem ich versuche mit einem Arduino Nano Every zwei ArduCAM Mini 5MP OV5642 anzusteuern, welche jeweils nacheinander ein Bild aufnehmen und anschließend über ein Adafruit MicroSD-Kartenmodul als JPEG auf eine MicroSD speichern.

Die einzelnen Komponenten sind über SPI mit dem Microkontroller verbunden und werden von diesem mit Strom versorgt.

Da ich wenig Erfahrung mit Arduino und der Ansteuerung von SD-Kartenmodulen habe, besteht das verwendete Skript größtenteils aus Teilen der ArduCAM Mini Bibliotheken für die OV5642.

#include <Wire.h>
#include <ArduCAM.h>
#include <SPI.h>
#include <SD.h>
#include "memorysaver.h"

//This demo can only work on OV5640_MINI_5MP_PLUS or OV5642_MINI_5MP_PLUS platform.
#if !(defined (OV5640_MINI_5MP_PLUS)||defined (OV5642_MINI_5MP_PLUS))
#error Please select the hardware platform and camera module in the ../libraries/ArduCAM/memorysaver.h file
#endif
#define SD_CS 14

// set CS-Ports of the cameras
const int CS1 = 10;
const int CS2 = 9;

bool CAM1_EXIST = false; 
bool CAM2_EXIST = false;

// set resoultion of the image
uint8_t resolution = OV5642_1280x960;
uint32_t line, column;

#if defined (OV5640_MINI_5MP_PLUS)
  ArduCAM myCAM1(OV5640, CS1);
  ArduCAM myCAM2(OV5640, CS2);
#else
  ArduCAM myCAM1(OV5642, CS1);
  ArduCAM myCAM2(OV5642, CS2);
#endif


//function for initializing both cameras
void initializeCAM() {
	
uint8_t vid, pid;
uint8_t temp;
Wire.begin(); 
Serial.begin(115200);
Serial.println(F("ArduCAM Start!")); 
// set the CS output:
pinMode(CS1, OUTPUT);
digitalWrite(CS1, HIGH);
pinMode(CS2, OUTPUT);
digitalWrite(CS2, HIGH);

//initialize SPI:
SPI.begin(); 

//Reset the CPLD
myCAM1.write_reg(0x07, 0x80);
delay(100);
myCAM1.write_reg(0x07, 0x00);
delay(100); 
myCAM2.write_reg(0x07, 0x80);
delay(100);
myCAM2.write_reg(0x07, 0x00);
delay(100); 

//Check if the 2 ArduCAM Mini 5MP PLus Cameras' SPI bus is OK
while(1){
  myCAM1.write_reg(ARDUCHIP_TEST1, 0x55);
  temp = myCAM1.read_reg(ARDUCHIP_TEST1);
  if(temp != 0x55)
  {
    Serial.println(F("SPI1 interface Error!"));
  }else{
    CAM1_EXIST = true;
    Serial.println(F("SPI1 interface OK."));
  }
  myCAM2.write_reg(ARDUCHIP_TEST1, 0x55);
  temp = myCAM2.read_reg(ARDUCHIP_TEST1);
  if(temp != 0x55)
  {
    Serial.println(F("SPI2 interface Error!"));
  }else{
    CAM2_EXIST = true;
    Serial.println(F("SPI2 interface OK."));
  }
  if(!(CAM1_EXIST||CAM2_EXIST)){
  delay(1000);continue;
  }else
  break;
}
//Initialize SD Card
while(!SD.begin(SD_CS)){
  Serial.println(F("SD Card Error"));delay(1000);
}
Serial.println(F("SD Card detected."));

#if defined (OV5640_MINI_5MP_PLUS)
  while(1){
    //Check if the camera module type is OV5640
    myCAM1.rdSensorReg16_8(OV5640_CHIPID_HIGH, &vid);
    myCAM1.rdSensorReg16_8(OV5640_CHIPID_LOW, &pid);
    if ((vid != 0x56) || (pid != 0x40)){
      Serial.println(F("Can't find OV5640 module!"));
      delay(1000);continue;
    }else{
      Serial.println(F("OV5640 detected."));break;
    }   
  }
#else
  while(1){
    //Check if the camera module type is OV5642
    myCAM1.rdSensorReg16_8(OV5642_CHIPID_HIGH, &vid);
    myCAM1.rdSensorReg16_8(OV5642_CHIPID_LOW, &pid);
    if ((vid != 0x56) || (pid != 0x42)){
      Serial.println(F("Can't find OV5642 module!"));
      delay(1000);continue;
    }else{
      Serial.println(F("OV5642 detected."));break;
    }  
  }
#endif
//Change to JPEG capture mode and initialize the OV5640 module
myCAM1.set_format(JPEG);
myCAM1.InitCAM();
myCAM1.write_reg(ARDUCHIP_TIM, VSYNC_LEVEL_MASK);   //VSYNC is active HIGH
myCAM2.write_reg(ARDUCHIP_TIM, VSYNC_LEVEL_MASK);   //VSYNC is active HIGH
}	

void captureImage(ArduCAM myCAM) {
	
  myCAM.flush_fifo();
  myCAM.clear_fifo_flag();
  myCAM.OV5642_set_JPEG_size(resolution);delay(1000);  
  
//Start capture
myCAM.start_capture();
Serial.println(F("Start capture."));
float capture_start_time = millis()/1000.0;
Serial.println("Mission Time: ");Serial.println(capture_start_time);
while ( !myCAM.get_bit(ARDUCHIP_TRIG, CAP_DONE_MASK)); 
Serial.println(F("CAM Capture Done."));
float capture_done_time = millis()/1000.0;
float capture_consumed_time = capture_done_time - capture_start_time;
Serial.println("Time needed for capturing image: ");Serial.println(capture_consumed_time);	
}

void saveImageToSD(ArduCAM myCAM) {
	
   File outFile;
    char VL;
    char str[8];
    byte buf[256];
    static int k = 0,m = 0;
    int i,j = 0;	
	
Serial.println("Saving the image,please waitting..."); 
float saving_start_time = millis()/1000.0;
Serial.println("Mission Time: ");Serial.println(saving_start_time);  
    itoa(saving_start_time, str, 10); 
    strcat(str,".jpg");        //Generate file name
    outFile = SD.open(str,O_WRITE | O_CREAT | O_TRUNC);
    if (! outFile) 
    {
      Serial.println(F("File open error"));
      return;
    }
    if(resolution == OV5642_640x480 ){
      line = 640;column = 480;
      }else if( resolution == OV5642_1280x960 ){
        line = 1280;column = 960;
        }else if( resolution == OV5642_1920x1080 ){
           line = 1920;column = 1080;
          }else if( resolution == OV5642_2592x1944 ){
            line = 2592;column = 1944;
            }
    //Save as JPEG format
    for(i = 0; i < line; i++)
    for(j = 0; j < column; j++)
    {
      VL = myCAM.read_fifo();
      buf[m++] = VL;
      if(m >= 256)
      {
        //Write 256 bytes image data to file from buffer
        outFile.write(buf,256);
        m = 0;
      }
    }
    if(m > 0 )//Write the left image data to file from buffer
      outFile.write( buf, m );m = 0;
    //Close the file  
    outFile.close(); 
    Serial.println("Image save OK.");
    float saving_done_time = millis()/1000.0;
    float saving_consumed_time = saving_done_time - saving_start_time;
    Serial.println("Time needed for saving file: ");Serial.println(saving_consumed_time);
//Clear the capture done flag
myCAM.clear_fifo_flag();
delay(10);
}




void setup() {
	
initializeCAM();

}


void loop() {
	
	if(CAM1_EXIST){
		captureImage(myCAM1);
		saveImageToSD(myCAM1);
	}
	if(CAM2_EXIST){
		captureImage(myCAM2); 
		saveImageToSD(myCAM2);
	}
}

Die Ansteuerung der Kameras klappt soweit, allerdings stoße ich beim durchlaufen des Programms auf das Problem, dass das schreiben der Bilder auf die MicroSD für meine Verwendung viel zu lange dauert ( bei einer Auflösung von 1280x960 Pixeln 27 Sekunden, bei 2592x1944 107 Sekunden und selbst bei minimaler Auflösung von 640x480 noch 6 Sekunden). Wenn ich das Format auf RAW statt JPEG setze, komme ich auf fast identische Zeiten.

Könnte man das Skript optimieren, sodass die speicherzeiten deutlich kürzer sind oder liegt es an der Hardware?

Schonmal vielen Dank für die Hilfe und freundliche Grüße :slight_smile:

Bist Du Dir sicher, das es die Speicherzeiten sind und nicht die Lesezeiten?

    {
      VL = myCAM.read_fifo();
      buf[m++] = VL;
      if (m >= 256)
      {
        //Write 256 bytes image data to file from buffer
        outFile.write(buf, 256);
        m = 0;
      }
    }

Ich würde das mal ändern:

    {
// Hier Zeit merken => Startzeit lesen
      VL = myCAM.read_fifo();
      buf[m++] = VL;
      if (m >= 256)
      {
// Hier Zeit merken => Endzeit lesen == Startzeit schreiben
        //Write 256 bytes image data to file from buffer
        outFile.write(buf, 256);
        m = 0;
      }
// Hier Zeit merken => Endzeit schreiben
// Differenzen errechnen und ausgaben
// Zeit3-Zeit2 = Schreibzeit
// Zeit2-Zeit1 = Lesezeit 
    }

Habe die Schreib- und Lesezeiten nun gemessen: Das Schreiben benötigt pro Wiederholung in der Schleife 4ms. Was die Lesegeschwindigkeit angeht bin ich mir nicht ganz sicher, da fast durchgehend 0.00ms gemessen wird und nur vereinzelt dann mal 1ms.

yeetility:
Das Schreiben benötigt pro Wiederholung in der Schleife 4ms.

Hm.
Also wenn Du 6 Sekunden für 640*480 schreibst, dann sind das
6000/4=1500 Schreibvorgänge.
In die kommen 255 256 Bytes. Macht: 382500 384000 Bytes.

Wie gross sind denn die Datein nachher tatsächlich?

[edit]
also ich hab ein massives Problem mit den 4ms.
Ich komm auf nichtmal die Hälfte:

19:32:57.667 -> 3396
19:32:57.700 -> 180
19:32:57.733 -> 3392
19:32:57.790 -> 184
19:32:57.790 -> 3388
19:32:57.799 -> 180
19:32:57.832 -> 3392
19:32:57.866 -> 180
19:32:57.899 -> 3388

Das sind microsekunden! auf einem W5100/SD Shield mit ner micro-sd mit folgendem Code:

#include <SPI.h>
#include <SD.h>

const int chipSelect = 4;
File outFile;
void setup()
{
  Serial.begin(115200);
  Serial.println(F("Start..."));
  SD.begin(chipSelect);
    outFile = SD.open("datalog.txt", FILE_WRITE);

}

void loop() {
  ausgabe();
}


void ausgabe()
{
  static byte buf[255];
  byte VL = 0;
  static int m = 0;
  randomSeed(millis());
  VL = random (65, 120);
  unsigned long startzeit = 0;
  unsigned long endzeit = 0;
  buf[m++] = VL;
  if (m >= 255)
  {
    startzeit = micros();
    outFile.write(buf, sizeof(buf));
    endzeit = micros();
    m = 0;
    Serial.println(endzeit - startzeit);
  }
}

In die kommen 255 Bytes. Macht: 382500 Bytes.

Sind das nicht 256 Bytes?
Grüße Uwe

uwefed:
Sind das nicht 256 Bytes?

Ich brech in Tränen aus.
Ja natürlich - ich hab heute soviele 255 gesehn....

my_xy_projekt:
Ich brech in Tränen aus.
Ja natürlich - ich hab heute soviele 255 gesehn....

So schlimm ist das nun auch wieder nicht.

Ja, zu den 255 muß man die Null dazuzählen und dann ergibt es 256 (verschiedene Zustände). :wink: :wink: :wink:

Grüße Uwe

Die 640x480 JPEGs sind 300kB groß.

Wenn ich "VL = myCAM.read_fifo();" weglasse komme ich auf ähnliche Zeiten. Das wäre dann schonmal 1,5x so schnell, was bei Bildern mit höherer Auflösung von z.B. 2592x1944 Pixeln mit ca. 70 Sekunden immernoch recht lange ist.

Könnte man mit einem besseren SD-Modul da noch einen nennenswerten Unterschied rausholen?

yeetility:
Wenn ich "VL = myCAM.read_fifo();" weglasse komme ich auf ähnliche Zeiten. Das wäre dann schonmal 1,5x so schnell, was bei Bildern mit höherer Auflösung von z.B. 2592x1944 Pixeln mit ca. 70 Sekunden immernoch recht lange ist.

Ich hab da noch das eine oder andere delay() gesehen, da schau ich schon nicht mehr drauf...
Der gleiche Sketch:

22:24:24.682 -> 2304
22:24:24.715 -> 160
22:24:24.748 -> 2304
22:24:24.748 -> 160
22:24:24.782 -> 2304
22:24:24.815 -> 160
22:24:24.848 -> 2316
22:24:24.848 -> 168
22:24:24.881 -> 2316
22:24:24.914 -> 160

Nur die lib geändert.

Damit dürfte nochmal einiges zusammenkommen.
Die Änderung ist marginal eine Zeile ändern, eine Zeile neu schreiben:

#include <SPI.h>
#include <SdFat.h>
SdFat SD;

Danke für die Hilfe soweit! :slight_smile:

Habe versucht die SdFat Lib einzubinden, erhalte jedoch immer folgende Fehlermeldungen in Bezug auf SdSpiAvr.h beim Kompilieren für den Nano Every:

...note: suggested alternative: 'SPI'
...error: 'SPSR' was not declared in this scope
...error: 'SPIF' was not declared in this scope

Hier der aktuelle Code:

#include <Wire.h>
#include <ArduCAM.h>
#include "memorysaver.h"
#include <SPI.h>
#include <SdFat.h>
SdFat SD;

//This demo can only work on OV5640_MINI_5MP_PLUS or OV5642_MINI_5MP_PLUS platform.
#if !(defined (OV5640_MINI_5MP_PLUS)||defined (OV5642_MINI_5MP_PLUS))
#error Please select the hardware platform and camera module in the ../libraries/ArduCAM/memorysaver.h file
#endif

#define SD_CS 14

// set CS-Ports of the cameras
const int CS1 = 10;
const int CS2 = 9;

bool CAM1_EXIST = false; 
bool CAM2_EXIST = false;

// set resoultion of the image
uint8_t resolution = OV5642_1280x960;
uint32_t line, column;

#if defined (OV5640_MINI_5MP_PLUS)
  ArduCAM myCAM1(OV5640, CS1);
  ArduCAM myCAM2(OV5640, CS2);
#else
  ArduCAM myCAM1(OV5642, CS1);
  ArduCAM myCAM2(OV5642, CS2);
#endif

File outFile;


//function for initializing both cameras
void initializeCAM() {
	
uint8_t vid, pid;
uint8_t temp;
Wire.begin(); 
Serial.begin(115200);
Serial.println(F("ArduCAM Start!")); 
// set the CS output:
pinMode(CS1, OUTPUT);
digitalWrite(CS1, HIGH);
pinMode(CS2, OUTPUT);
digitalWrite(CS2, HIGH);

//initialize SPI:
SPI.begin(); 

//Reset the CPLD
myCAM1.write_reg(0x07, 0x80);
delay(100);
myCAM1.write_reg(0x07, 0x00);
delay(100); 
myCAM2.write_reg(0x07, 0x80);
delay(100);
myCAM2.write_reg(0x07, 0x00);
delay(100); 

//Check if the 2 ArduCAM Mini 5MP PLus Cameras' SPI bus is OK
while(1){
  myCAM1.write_reg(ARDUCHIP_TEST1, 0x55);
  temp = myCAM1.read_reg(ARDUCHIP_TEST1);
  if(temp != 0x55)
  {
    Serial.println(F("SPI1 interface Error!"));
  }else{
    CAM1_EXIST = true;
    Serial.println(F("SPI1 interface OK."));
  }
  myCAM2.write_reg(ARDUCHIP_TEST1, 0x55);
  temp = myCAM2.read_reg(ARDUCHIP_TEST1);
  if(temp != 0x55)
  {
    Serial.println(F("SPI2 interface Error!"));
  }else{
    CAM2_EXIST = true;
    Serial.println(F("SPI2 interface OK."));
  }
  if(!(CAM1_EXIST||CAM2_EXIST)){
  delay(1000);continue;
  }else
  break;
}
//Initialize SD Card
while(!SD.begin(SD_CS)){
  Serial.println(F("SD Card Error"));delay(1000);
}
Serial.println(F("SD Card detected."));

#if defined (OV5640_MINI_5MP_PLUS)
  while(1){
    //Check if the camera module type is OV5640
    myCAM1.rdSensorReg16_8(OV5640_CHIPID_HIGH, &vid);
    myCAM1.rdSensorReg16_8(OV5640_CHIPID_LOW, &pid);
    if ((vid != 0x56) || (pid != 0x40)){
      Serial.println(F("Can't find OV5640 module!"));
      delay(1000);continue;
    }else{
      Serial.println(F("OV5640 detected."));break;
    }   
  }
#else
  while(1){
    //Check if the camera module type is OV5642
    myCAM1.rdSensorReg16_8(OV5642_CHIPID_HIGH, &vid);
    myCAM1.rdSensorReg16_8(OV5642_CHIPID_LOW, &pid);
    if ((vid != 0x56) || (pid != 0x42)){
      Serial.println(F("Can't find OV5642 module!"));
      delay(1000);continue;
    }else{
      Serial.println(F("OV5642 detected."));break;
    }  
  }
#endif
//Change to JPEG capture mode and initialize the OV5640 module
myCAM1.set_format(JPEG);
myCAM1.InitCAM();
myCAM1.write_reg(ARDUCHIP_TIM, VSYNC_LEVEL_MASK);   //VSYNC is active HIGH
myCAM2.write_reg(ARDUCHIP_TIM, VSYNC_LEVEL_MASK);   //VSYNC is active HIGH
}	

void captureImage(ArduCAM myCAM) {
	
  myCAM.flush_fifo();
  myCAM.clear_fifo_flag();
  myCAM.OV5642_set_JPEG_size(resolution);delay(1000);  
  
//Start capture
myCAM.start_capture();
Serial.println(F("Start capture."));
float capture_start_time = millis()/1000.0;
Serial.println("Mission Time: ");Serial.println(capture_start_time);
while ( !myCAM.get_bit(ARDUCHIP_TRIG, CAP_DONE_MASK)); 
Serial.println(F("CAM Capture Done."));
float capture_done_time = millis()/1000.0;
float capture_consumed_time = capture_done_time - capture_start_time;
Serial.println("Time needed for capturing image: ");Serial.println(capture_consumed_time);	
}

void saveImageToSD(ArduCAM myCAM) {
	
    byte VL;
    char str[8];
    static byte buf[256];
    static int m = 0;
    int i,j = 0;	
	randomSeed(millis());
	unsigned long start_time = 0;
	unsigned long end_time = 0;
	
Serial.println("Saving the image,please waitting..."); 
float saving_start_time = millis()/1000.0;
Serial.println("Mission Time: ");Serial.println(saving_start_time);  

    itoa(saving_start_time, str, 10); 
    strcat(str,".jpg");        //Generate file name
	
    outFile = SD.open(str,O_WRITE | O_CREAT | O_TRUNC);
	
    if (! outFile) 
    {
      Serial.println(F("File open error"));
      return;
    }
	
    if(resolution == OV5642_640x480 ){
      line = 640;column = 480;
      }else if( resolution == OV5642_1280x960 ){
        line = 1280;column = 960;
        }else if( resolution == OV5642_1920x1080 ){
           line = 1920;column = 1080;
          }else if( resolution == OV5642_2592x1944 ){
            line = 2592;column = 1944;
            }
			
    //Save as JPEG format
    for(i = 0; i < line; i++)
    for(j = 0; j < column; j++)
    {
      //VL = myCAM.read_fifo();
      buf[m++] = VL;
      if(m >= 256)
      {
		start_time = micros();
        outFile.write(buf,sizeof(buf));
		end_time = micros();
        m = 0;
		Serial.println(end_time - start_time);
      }
    }
    if(m > 0 )//Write the left image data to file from buffer
      outFile.write( buf, m );m = 0;
    //Close the file  
    outFile.close(); 
    Serial.println("Image save OK.");
    float saving_done_time = millis()/1000.0;
    float saving_consumed_time = saving_done_time - saving_start_time;
    Serial.println("Time needed for saving file: ");Serial.println(saving_consumed_time);
//Clear the capture done flag
myCAM.clear_fifo_flag();
}




void setup() {
	
initializeCAM();

}


void loop() {
	
	if(CAM1_EXIST){
		captureImage(myCAM1);
		saveImageToSD(myCAM1);
	}
	if(CAM2_EXIST){
		captureImage(myCAM2); 
		saveImageToSD(myCAM2);
	}  
}

yeetility:
Habe versucht die SdFat Lib einzubinden, erhalte jedoch immer folgende Fehlermeldungen in Bezug auf SdSpiAvr.h beim Kompilieren für den Nano Every:

Ist es möglich einen Schritt nach dem anderen zu machen?
Erstmal schauen, ob das überhaupt relevant ist.

//#define tik

#include <SPI.h>
// Variante 1
// #include <SD.h>
// ODER! Variante 2
#include <SdFat.h>
SdFat SD;
// Ende Variante 2

const int chipSelect = 4;
const int bufsize = 255;

File outFile;
void setup()
{
  Serial.begin(115200);
  Serial.println(F("Start..."));
  SD.begin(chipSelect);
  outFile = SD.open("datalog.txt", FILE_WRITE);
  outFile.remove();
  outFile.close();
  outFile = SD.open("datalog.txt", FILE_WRITE);

}

void loop() {
  static unsigned long lastmillis = millis();
  static unsigned long durchlauf = 0;
  ausgabe();
  durchlauf++;
  if (millis() - lastmillis > 5000)
  {
    unsigned long stopmillis = millis();
    outFile.close();
    Serial.print(F("Anzahl Durchläufe: "));
    Serial.print(durchlauf);
    Serial.print(F(" in ms: "));
    Serial.println(stopmillis - lastmillis);
    while (1);
  }
}


void ausgabe()
{
  static byte buf[bufsize];
  byte VL = 0;
  static int m = 0;
  randomSeed(micros());
  VL = random (65, 89);
  buf[m] = VL;
  m++;
  if (m >= bufsize)
  {
#ifdef tik
    unsigned long startzeit = millis();
#endif
    outFile.write(buf, bufsize);
#ifdef tik
    unsigned long endzeit = millis();
    Serial.println(endzeit - startzeit);
#endif
    m = 0;
  }
}

In der ersten Zeile kannst Du den Kommentar entfernen.
Dann geht für 5 Sekunden der Sketch - Ausgabe der Zeit fürs Schreiben und am Ende die Gesamtzeit mit der Anzahl der Durchläufe.
Wieder einkommentiert, bekommst für (fast) die selbe Zeit die Anzahl der Durchläufe.
Bei beiden Durchläufen mal vergleichen, wie gross die tatsächlich geschriebene Datei ist.

Auch nach einiger Überlegungszeit denke ich nicht, das es Dir reichen wird.
Aber die SdFat hat noch einige Überraschungen,was das Handling angeht. Da ich das nicht brauche und auch nie brauchen werde, ist das hier auch nie angefasst worden.
Bitte dazu in den Beispielen nachsehen.

Und unbedingt drauf achten, das die Karte das auch von der Geschwindigkeit hergibt.

This topic was automatically closed 120 days after the last reply. New replies are no longer allowed.