Arducam Mini 2MP Plus module intermittently fails to capture an image when used with an Adafruit Feather M4 Express

Hello, I am experiencing some issues with attempting to use an Arducam Mini 2MP Plus camera module (purchased from the official listing on Amazon.com) with an Adafruit Feather M4 Express. I am attempting to capture a single photo and save it to a microSD card on an external module, and I am using a modified version of the example sketch “Arducam_Mini_2MP_Plus_Multi_Capture2SD” to do so. Here is the sketch being used:

// ArduCAM Mini demo (C)2018 Lee
// Web: http://www.ArduCAM.com
// This program is a demo of how to use the enhanced functions
// This demo was made for ArduCAM_Mini_2MP_Plus.
// It can  continue shooting and store it into the SD card  in JPEG format
// The demo sketch will do the following tasks
// 1. Set the camera to JPEG output mode.
// 2. Capture a JPEG photo and buffer the image to FIFO
// 3.Write the picture data to the SD card
// 5.close the file
//You can change the FRAMES_NUM count to change the number of the picture.
//IF the FRAMES_NUM is 0X00, take one photos
//IF the FRAMES_NUM is 0X01, take two photos
//IF the FRAMES_NUM is 0X02, take three photos
//IF the FRAMES_NUM is 0X03, take four photos
//IF the FRAMES_NUM is 0X04, take five photos
//IF the FRAMES_NUM is 0X05, take six photos
//IF the FRAMES_NUM is 0X06, take seven photos
//IF the FRAMES_NUM is 0XFF, continue shooting until the FIFO is full
//You can see the picture in the SD card.
// This program requires the ArduCAM V4.0.0 (or later) library and ArduCAM_Mini_2MP_Plus
// and use Arduino IDE 1.6.8 compiler or above

#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.

#define   FRAMES_NUM    0x00

// set pin 7 as the slave select for the digital pot:
const int CS = 10;

#define SD_CS 11 // For Adalogger Feather M0, NOT HYPNOS
#define HYPNOS_5VR 6
#define HYPNOS_3VR 5
#define AMP_SD 9

bool is_header = false;
int total_time = 0;

ArduCAM myCAM( OV2640, CS );

uint8_t read_fifo_burst(ArduCAM myCAM);

void setup() {
  delay(4000);
  
  // put your setup code here, to run once:
  uint8_t vid, pid;
  uint8_t temp;

  Serial.begin(115200);
  Serial.println(F("ArduCAM Start!"));
  
  // set the CS as an output:
  pinMode(SD_CS, OUTPUT);
  pinMode(CS, OUTPUT);
  digitalWrite(CS, HIGH);
  
  // Enables the 5 volt rail and the 3 volt rail on the external power control board,
  // which provides power to the camera module and SD card.
  pinMode(HYPNOS_5VR, OUTPUT);
  pinMode(HYPNOS_3VR, OUTPUT);
  pinMode(AMP_SD, OUTPUT);

  digitalWrite(HYPNOS_5VR, HIGH);
  digitalWrite(HYPNOS_3VR, LOW);
  digitalWrite(AMP_SD, LOW);
  
  // initialize SPI:
  //SPI.beginTransaction(SPISettings(4000000, MSBFIRST, SPI_MODE0));
  SPI.begin();
  
  Wire.begin();
  
  //Reset the CPLD
  myCAM.write_reg(0x07, 0x80);
  delay(100);
  
  myCAM.write_reg(0x07, 0x00);
  delay(100);
  
  while (1) {
    //Check if the ArduCAM SPI bus is OK
    myCAM.write_reg(ARDUCHIP_TEST1, 0x55);
    temp = myCAM.read_reg(ARDUCHIP_TEST1);
    if (temp != 0x55)
    {
      Serial.println(F("SPI interface Error!"));
      Serial.println(temp);
      delay(1000);
      continue;
    } else {
      Serial.println(F("SPI interface OK.")); break;
    }
  }

  while (1) {
    //Check if the camera module type is OV2640
    myCAM.wrSensorReg8_8(0xff, 0x01);
    myCAM.rdSensorReg8_8(OV2640_CHIPID_HIGH, &vid);
    myCAM.rdSensorReg8_8(OV2640_CHIPID_LOW, &pid);
    if ((vid != 0x26 ) && (( pid != 0x41 ) || ( pid != 0x42 ))) {
      Serial.println(F("ACK CMD Can't find OV2640 module!"));
      delay(1000); continue;
    }
    else {
      Serial.println(F("ACK CMD OV2640 detected.")); break;
    }
  }

      //Initialize SD Card
  while (!SD.begin(SD_CS))
  {
    Serial.println(F("SD Card Error!")); delay(250);
    break;
  }
  
  Serial.println(F("SD Card detected.\n"));


  myCAM.set_format(JPEG);
  myCAM.InitCAM();
  myCAM.clear_fifo_flag();
  myCAM.write_reg(ARDUCHIP_FRAMES, FRAMES_NUM);
  myCAM.OV2640_set_JPEG_size(OV2640_1600x1200);
  //
}

void loop() {
  
  myCAM.flush_fifo();
  delay(10);
  
  myCAM.clear_fifo_flag();
  delay(10);
  
  //Start capture
  myCAM.start_capture();
  
  Serial.println(F("start capture."));
  
  total_time = millis();
  
  while ( !myCAM.get_bit(ARDUCHIP_TRIG, CAP_DONE_MASK)){};
  
  Serial.println(F("CAM Capture Done."));
  
  total_time = millis() - total_time;
  Serial.print(F("capture total_time used (in miliseconds):"));
  Serial.println(total_time, DEC);
  
  total_time = millis();
  read_fifo_burst(myCAM);
  
  total_time = millis() - total_time;
  Serial.print(F("save capture total_time used (in miliseconds):"));
  Serial.println(total_time, DEC);
  Serial.println();
  
  //Clear the capture done flag
  myCAM.clear_fifo_flag();
  delay(10);

  delay(500);
}


uint8_t read_fifo_burst(ArduCAM myCAM)
{
  uint8_t temp = 0, temp_last = 0;
  uint32_t length = 0;
  static int i = 0;
  static int k = 0;
  char str[16];
  File outFile;
  byte buf[256];
  
  length = myCAM.read_fifo_length();
  
  Serial.print(F("The fifo length is :"));
  Serial.println(length, DEC);
  
  if (length >= MAX_FIFO_SIZE) //8M
  {
    Serial.println("Over size.");
    return 0;
  }
  
  if (length == 0 ) //0 kb
  {
    Serial.println(F("Size is 0."));
    return 0;
  }
  
  digitalWrite(CS, LOW);
  myCAM.set_fifo_burst();//Set fifo burst mode
  i = 0;
  
  while ( length-- )
  {
    temp_last = temp;
    temp =  SPI.transfer(0x00);
    
    //End of the image transmission
    if ( (temp == 0xD9) && (temp_last == 0xFF) ) //If find the end ,break while,
    {
      buf[i++] = temp;  //save the last  0XD9
      
      //Write the remain bytes in the buffer
      digitalWrite(CS, HIGH);
      outFile.write(buf, i);
      
      //Close the file
      outFile.close();
      Serial.print(F("OK: "));
      Serial.println(length);
      
      is_header = false;
      digitalWrite(CS, LOW);
      myCAM.set_fifo_burst();
      i = 0;
    }
    
    if (is_header == true)
    {
      //Write image data to buffer if not full
      if (i < 256)
        buf[i++] = temp;
      else
      {
        //Write 256 bytes image data to file
        digitalWrite(CS, HIGH);
        outFile.write(buf, 256);
        i = 0;
        buf[i++] = temp;
        digitalWrite(CS, LOW);
        myCAM.set_fifo_burst();
      }
    }

    // Beginning of the image transmission
    else if ((temp == 0xD8) & (temp_last == 0xFF)) // This is the start of a jpg file header
    {
      is_header = true;
      digitalWrite(CS, HIGH);
      
      //Assembles filename
      k = k + 1;
      itoa(k, str, 10);
      strcat(str, ".jpg");
      
      //Open the new file
      outFile = SD.open(str, O_WRITE | O_CREAT | O_TRUNC);
      
      if (! outFile)
      {
        Serial.println(F("File open failed"));
        while (1);
      }
      
      digitalWrite(CS, LOW);
      myCAM.set_fifo_burst();
      buf[i++] = temp_last;
      buf[i++] = temp;
    }
  }
  
  digitalWrite(CS, HIGH);
  return 1;
}

The primary differences between this sketch and the original example sketch are:

  • Digital writes are being made to pins 5 and 6 in setup(), which command an external power control board to begin providing power to the camera module and the microSD card.

  • Moving the command myCAM.OV2640_set_JPEG_size(OV2640_1600x1200) from the main loop into setup(), as it did not appear to be necessary to call for every photo (I have already tried putting this back into the main loop, and it did not fix the problem)

  • Adding small delays between each of the register writes in the main loop.

  • Reformatting.

The problem is that after capturing and storing between 1-5 photos, the program will become stuck after printing “capture start” to the console, indicating that the camera module never sets the capture done bit to 1 (or possibly that the microcontroller is failing to read the register properly.) An example of the output produced by the sketch is shown below:

ArduCAM Start!
SPI interface OK.
ACK CMD OV2640 detected.
SD Card detected.

start capture.
CAM Capture Done.
capture total_time used (in miliseconds):359
The fifo length is :51208
OK: 689
save capture total_time used (in miliseconds):415

start capture.
CAM Capture Done.
capture total_time used (in miliseconds):173
The fifo length is :52232
OK: 316
save capture total_time used (in miliseconds):435

start capture.
CAM Capture Done.
capture total_time used (in miliseconds):154
The fifo length is :52232
OK: 274
save capture total_time used (in miliseconds):449

start capture.

The odd thing is that the sketch works perfectly on an Adafruit Feather M0. When running it on an M0, it is able to reliably capture >300 photos without suffering from the same problem.

The primary differences between the M0 and the M4 microcontrollers is that the M0 is SAMD21 based and runs at 48 MHz, while the M4 is SAMD51 based and runs at 120 MHz. The different boards have the exact same pin layout and are otherwise interchangeable.

My original suspicion was that either the SPI or the I2C clock frequency was being set too high on the M4, but setting both of these values to 2 MHz and 100 kHz respectively did not fix the problem.

I am confident that this is not a wiring and/or power problem: both the camera module and the microcontroller are being installed into a purpose-built PCB that connects the two. An image of the setup is provided below:

I am unsure as to what would cause the camera module to behave differently between the two microcontrollers other than a difference in how either handles SPI/I2C communication. I am going to try to use software SPI on the M4 soon (and I will report back on whether or not it solves the issue), but I would appreciate any information on what else the source of the problem could be.

Hi,
Thanks for your contacting us. I think your original suspicion is right. Are you sure the spi speed is 2M? Is there a way to measure the clock speed?