ArduCAM / Arduino

This is ArduCAM library for Arduino boards
MIT License
479 stars 351 forks source link

ARDUCAM with Crazyflie 2.0. STM32 #127

Open jassi0709 opened 7 years ago

jassi0709 commented 7 years ago

Hello everybody. I'm working on integrating the Arducam with an opensource drone called Crazyflie 2.0. The latter uses an STM32 with DMA controller enabled in the SPI communication.

After connecting and porting the code from Arduino to STM32 SPI API, I've noticed a really strange behavior when I try to read the TEST register: I read something different from what I wrote.

Comes without saying that, on Arduino, everything is working flawlessly, while on the STM32 the camera is replying with a wrong signal (see 4BothRead0x00_2.png and 4BothRead0x00_2_Enhanced.png).

The high-level code used for Arduino is: delay(5000); myCAM.write_reg(ARDUCHIP_TEST1, 0x40); temp = myCAM.read_reg(ARDUCHIP_TEST1); Serial.print("Test: ");Serial.println(temp, HEX);

The high-level code used for STM32 is: vTaskDelay(M2T(5000)); write_reg(ARDUCHIP_TEST1, 0x40); temp = read_reg(ARDUCHIP_TEST1); DEBUG_PRINT("Read SPI temp: %d %x\n", temp,temp); temp = read_reg(ARDUCHIP_REV); DEBUG_PRINT("REV: %d %x\n", temp,temp);

The rest is just exactly the same.

1bothwrite0x40_1 2bothwrite0x40_2 3bothread0x00_1 4bothread0x00_2 4bothread0x00_2_enhanced

I've tried all the following: 1) Change the ArduCAM 2) Use the same ArduCAM 3) Checked several time the hardware connections 4) Since the drone is powering the ArduCAM with 3.3V, I've tried to power up the camera with external 5V 5) Tried different combination of SPI settings (CPOL, CPHA, MSB, LSB etc) 6) Other small things.

Please let me know if you have any idea.

Best regards, Jaskirat

PS: In the pictures, the top oscilloscope screenshot is for from the Arduino while the bottom one is from the Crazyflie.

supprot commented 7 years ago

@jassi0709 Hi, Your pictures don't have detail instructinos and we can't understand it clearly. Please instruct each picture clearly.

When did you buy our product? Our old version need shift operation at the ARM platform. In our library, We do like this: value = SPI.transfer(0x00); value = (byte)(value >> 1) | (value << 7); Please take a picture of your camera to us.

Another, Our SPI mode is SPI0 which means you should set CPOL =0 and CPHA = 0.

Regards, ArduCAM support team.

jassi0709 commented 7 years ago

@supprot Hi support team. I've actually figured out the problem. It looks like that the delay between the "Command phase" and "Data phase" (datasheet page 4) matters a lot. I don't know why, but my STM32 was a bit too long in that, and therefore after comparing the timing with Arduino firmware, I've figured out that.

Moreover, it looks like that the shift operation is needed in the STM32 as well.

Now, reading the BMP with single FIFO read works correctly but, unfortunately, when I enter the burst mode, the data are corrupted a bit. I've already tried to put and remove the shift operation but it didn't help. Do you have any suggestion on that?

In addition I've seen that entering a different "Resolution mode" (register 0x12 | COM7) from UXGA to CIF or SVGA is quite a challenge. Do you have any example that work on Arduino and put the camera in CIF or SVGA mode in order to achieve higher frame rate?

You can find my camera pictures here. pic_20170118_090450 pic_20170118_090439

supprot commented 7 years ago

@jassi0709 Hi, Congratulations to you to solve that problem . You can send the bad image which our camera captured to us. We will help you check the image's data to solve the problem.

In adition, you can reference the example below. You should use our ArduCAM_Host_V2 which can be downloaded on the https://github.com/arducam website. The software is in the ArduCAM/host_app folder.

Codes: // ArduCAM Mini demo (C)2017 Lee // Web: http://www.ArduCAM.com // This program is a demo of how to use most of the functions // of the library with ArduCAM Mini camera, and can run on any Arduino platform. // This demo was made for ArduCAM_Mini_5MP_Plus. // It needs to be used in combination with PC software. // It can take photo continuously as video streaming. // // The demo sketch will do the following tasks: // 1. Set the camera to JPEG output mode. // 2. Read data from Serial port and deal with it // 3. If receive 0x00-0x08,the resolution will be changed. // 4. If receive 0x10,camera will capture a JPEG photo and buffer the image to FIFO.Then write datas to Serial port. // 5. If receive 0x20,camera will capture JPEG photo and write datas continuously.Stop when receive 0x21. // 6. If receive 0x30,camera will capture a BMP photo and buffer the image to FIFO.Then write datas to Serial port. // 7. If receive 0x11 ,set camera to JPEG output mode. // 8. If receive 0x31 ,set camera to BMP output mode. // This program requires the ArduCAM V4.0.0 (or later) library and ArduCAM_Mini_5MP_Plus // and use Arduino IDE 1.6.8 compiler or above

include

include

include

include "memorysaver.h"

//This demo can only work on OV2640_MINI_2MP or OV5642_MINI_5MP or OV5642_MINI_5MP_BIT_ROTATION_FIXED platform.

if !(defined OV5642_MINI_5MP || defined OV5642_MINI_5MP_BIT_ROTATION_FIXED || defined OV2640_MINI_2MP)

error Please select the hardware platform and camera module in the ../libraries/ArduCAM/memorysaver.h file

endif

define BMPIMAGEOFFSET 66

const char bmp_header[BMPIMAGEOFFSET] PROGMEM = { 0x42, 0x4D, 0x36, 0x58, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x42, 0x00, 0x00, 0x00, 0x28, 0x00, 0x00, 0x00, 0x40, 0x01, 0x00, 0x00, 0xF0, 0x00, 0x00, 0x00, 0x01, 0x00, 0x10, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x58, 0x02, 0x00, 0xC4, 0x0E, 0x00, 0x00, 0xC4, 0x0E, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xF8, 0x00, 0x00, 0xE0, 0x07, 0x00, 0x00, 0x1F, 0x00, 0x00, 0x00 }; // set pin 7 as the slave select for the digital pot: const int CS = 7; bool is_header = false; int mode = 0; uint8_t start_capture = 0;

if defined (OV2640_MINI_2MP)

ArduCAM myCAM( OV2640, CS );

else

ArduCAM myCAM( OV5642, CS );

endif

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

if defined(SAM3X8E)

Wire1.begin(); Serial.begin(115200);

else

Wire.begin(); Serial.begin(921600);

endif

Serial.println(F("ACK CMD ArduCAM Start!")); // set the CS as an output: pinMode(CS, OUTPUT); // initialize SPI: SPI.begin(); 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("ACK CMD SPI interface Error!")); delay(1000);continue; }else{ Serial.println(F("ACK CMD SPI interface OK."));break; } }

if defined (OV2640_MINI_2MP)

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; } }

else

while(1){ //Check if the camera module type is OV5642 myCAM.wrSensorReg16_8(0xff, 0x01); myCAM.rdSensorReg16_8(OV5642_CHIPID_HIGH, &vid); myCAM.rdSensorReg16_8(OV5642_CHIPID_LOW, &pid); if((vid != 0x56) || (pid != 0x42)){ Serial.println(F("ACK CMD Can't find OV5642 module!")); delay(1000);continue; } else{ Serial.println(F("ACK CMD OV5642 detected."));break; } }

endif

//Change to JPEG capture mode and initialize the OV5642 module myCAM.set_format(JPEG); myCAM.InitCAM();

if defined (OV2640_MINI_2MP)

myCAM.OV2640_set_JPEG_size(OV2640_320x240);

else

myCAM.write_reg(ARDUCHIP_TIM, VSYNC_LEVEL_MASK); //VSYNC is active HIGH myCAM.OV5642_set_JPEG_size(OV5642_320x240);

endif

delay(1000); myCAM.clear_fifo_flag();

if !(defined (OV2640_MINI_2MP))

myCAM.write_reg(ARDUCHIP_FRAMES,0x00);

endif

} void loop() { // put your main code here, to run repeatedly: uint8_t temp = 0xff, temp_last = 0; bool is_header = false; if (Serial.available()) { temp = Serial.read(); switch (temp) { case 0:

if defined (OV2640_MINI_2MP)

  myCAM.OV2640_set_JPEG_size(OV2640_160x120);delay(1000);
  Serial.println(F("ACK CMD switch to OV2640_160x120"));
#else
  myCAM.OV5642_set_JPEG_size(OV5642_320x240);delay(1000);
  Serial.println(F("ACK CMD switch to OV5642_320x240"));
#endif
temp = 0xff;
break;
case 1:
#if defined (OV2640_MINI_2MP)
  myCAM.OV2640_set_JPEG_size(OV2640_176x144);delay(1000);
  Serial.println(F("ACK CMD switch to OV2640_176x144"));
#else
  myCAM.OV5642_set_JPEG_size(OV5642_640x480);delay(1000);
  Serial.println(F("ACK CMD switch to OV5642_640x480"));
#endif
temp = 0xff;
break;
case 2: 
#if defined (OV2640_MINI_2MP)
  myCAM.OV2640_set_JPEG_size(OV2640_320x240);delay(1000);
  Serial.println(F("ACK CMD switch to OV2640_320x240"));
#else
  myCAM.OV5642_set_JPEG_size(OV5642_1024x768);delay(1000);
  Serial.println(F("ACK CMD switch to OV5642_1024x768"));
#endif
temp = 0xff;
break;
case 3:
temp = 0xff;
#if defined (OV2640_MINI_2MP)
  myCAM.OV2640_set_JPEG_size(OV2640_352x288);delay(1000);
  Serial.println(F("ACK CMD switch to OV2640_352x288"));
#else
  myCAM.OV5642_set_JPEG_size(OV5642_1280x960);delay(1000);
  Serial.println(F("ACK CMD switch to OV5642_1280x960"));
#endif
break;
case 4:
temp = 0xff;
#if defined (OV2640_MINI_2MP)
  myCAM.OV2640_set_JPEG_size(OV2640_640x480);delay(1000);
  Serial.println(F("ACK CMD switch to OV2640_640x480"));
#else
  myCAM.OV5642_set_JPEG_size(OV5642_1600x1200);delay(1000);
  Serial.println(F("ACK CMD switch to OV5642_1600x1200"));
#endif
break;
case 5:
temp = 0xff;
#if defined (OV2640_MINI_2MP)
  myCAM.OV2640_set_JPEG_size(OV2640_800x600);delay(1000);
  Serial.println(F("ACK CMD switch to OV2640_800x600"));
#else
  myCAM.OV5642_set_JPEG_size(OV5642_2048x1536);delay(1000);
  Serial.println(F("ACK CMD switch to OV5642_2048x1536"));
#endif
break;
case 6:
temp = 0xff;
#if defined (OV2640_MINI_2MP)
  myCAM.OV2640_set_JPEG_size(OV2640_1024x768);delay(1000);
  Serial.println(F("ACK CMD switch to OV2640_1024x768"));
#else
  myCAM.OV5642_set_JPEG_size(OV5642_2592x1944);delay(1000);
  Serial.println(F("ACK CMD switch to OV5642_2592x1944"));
#endif
break;
#if defined (OV2640_MINI_2MP)
case 7:
temp = 0xff;
myCAM.OV2640_set_JPEG_size(OV2640_1280x1024);delay(1000);
Serial.println(F("ACK CMD switch to OV2640_1280x1024"));
break;
case 8:
temp = 0xff;
myCAM.OV2640_set_JPEG_size(OV2640_1600x1200);delay(1000);
Serial.println(F("ACK CMD switch to OV2640_1600x1200"));
break;
#endif
case 0x10:
mode = 1;
temp = 0xff;
start_capture = 1;
Serial.println(F("ACK CMD CAM start single shoot."));
break;
case 0x11: 
temp = 0xff;
myCAM.set_format(JPEG);
myCAM.InitCAM();
#if !(defined (OV2640_MINI_2MP))
myCAM.set_bit(ARDUCHIP_TIM, VSYNC_LEVEL_MASK);
#endif
break;
case 0x20:
mode = 2;
temp = 0xff;
start_capture = 2;
Serial.println(F("ACK CMD CAM start video streaming."));
break;
case 0x30:
mode = 3;
temp = 0xff;
start_capture = 3;
Serial.println(F("CAM start single shoot."));
break;
case 0x31:
temp = 0xff;
myCAM.set_format(BMP);
myCAM.InitCAM();
#if !(defined (OV2640_MINI_2MP))        
myCAM.clear_bit(ARDUCHIP_TIM, VSYNC_LEVEL_MASK);
#endif
myCAM.wrSensorReg16_8(0x3818, 0x81);
myCAM.wrSensorReg16_8(0x3621, 0xA7);
break;
default:
break;

} } if (mode == 1) { if (start_capture == 1) { myCAM.flush_fifo(); myCAM.clear_fifo_flag(); //Start capture myCAM.start_capture(); start_capture = 0; } if (myCAM.get_bit(ARDUCHIP_TRIG, CAP_DONE_MASK)) { Serial.println(F("ACK CMD CAM Capture Done.")); read_fifo_burst(myCAM); //Clear the capture done flag myCAM.clear_fifo_flag(); } } else if (mode == 2) { while (1) { temp = Serial.read(); if (temp == 0x21) { start_capture = 0; mode = 0; Serial.println(F("ACK CMD CAM stop video streaming.")); break; } if (start_capture == 2) { myCAM.flush_fifo(); myCAM.clear_fifo_flag(); //Start capture myCAM.start_capture(); start_capture = 0; } if (myCAM.get_bit(ARDUCHIP_TRIG, CAP_DONE_MASK)) { uint32_t length = 0; length = myCAM.read_fifo_length(); if ((length >= MAX_FIFO_SIZE) | (length == 0)) { myCAM.clear_fifo_flag(); start_capture = 2; continue; } myCAM.CS_LOW(); myCAM.set_fifo_burst();//Set fifo burst mode temp = SPI.transfer(0x00); length --; while ( length-- ) { temp_last = temp; temp = SPI.transfer(0x00); if (is_header == true) { Serial.write(temp); } else if ((temp == 0xD8) & (temp_last == 0xFF)) { is_header = true; Serial.println(F("ACK IMG")); Serial.write(temp_last); Serial.write(temp); } if ( (temp == 0xD9) && (temp_last == 0xFF) ) //If find the end ,break while, break; delayMicroseconds(15); } myCAM.CS_HIGH(); myCAM.clear_fifo_flag(); start_capture = 2; is_header = false; } } } else if (mode == 3) { if (start_capture == 3) { //Flush the FIFO myCAM.flush_fifo(); myCAM.clear_fifo_flag(); //Start capture myCAM.start_capture(); start_capture = 0; } if (myCAM.get_bit(ARDUCHIP_TRIG, CAP_DONE_MASK)) { Serial.println(F("ACK CMD CAM Capture Done.")); uint8_t temp, temp_last; uint32_t length = 0; length = myCAM.read_fifo_length(); if (length >= MAX_FIFO_SIZE ) { Serial.println(F("ACK CMD Over size.")); myCAM.clear_fifo_flag(); return; } if (length == 0 ) //0 kb { Serial.println(F("ACK CMD Size is 0.")); myCAM.clear_fifo_flag(); return; } myCAM.CS_LOW(); myCAM.set_fifo_burst();//Set fifo burst mode

Serial.write(0xFF);
Serial.write(0xAA);
for (temp = 0; temp < BMPIMAGEOFFSET; temp++)
{
  Serial.write(pgm_read_byte(&bmp_header[temp]));
}
SPI.transfer(0x00);
char VH, VL;
int i = 0, j = 0;
for (i = 0; i < 240; i++)
{
  for (j = 0; j < 320; j++)
  {
    VH = SPI.transfer(0x00);;
    VL = SPI.transfer(0x00);;
    Serial.write(VL);
    delayMicroseconds(12);
    Serial.write(VH);
    delayMicroseconds(12);
  }
}
Serial.write(0xBB);
Serial.write(0xCC);
myCAM.CS_HIGH();
//Clear the capture done flag
myCAM.clear_fifo_flag();

} } } uint8_t read_fifo_burst(ArduCAM myCAM) { uint8_t temp = 0, temp_last = 0; uint32_t length = 0; length = myCAM.read_fifo_length(); Serial.println(length, DEC); if (length >= MAX_FIFO_SIZE) //512 kb { Serial.println(F("Over size.")); return 0; } if (length == 0 ) //0 kb { Serial.println(F("Size is 0.")); return 0; } myCAM.CS_LOW(); myCAM.set_fifo_burst();//Set fifo burst mode temp = SPI.transfer(0x00); length --; while ( length-- ) { temp_last = temp; temp = SPI.transfer(0x00); if (is_header == true) { Serial.write(temp); } else if ((temp == 0xD8) & (temp_last == 0xFF)) { is_header = true; Serial.println(F("ACK IMG")); Serial.write(temp_last); Serial.write(temp); } if ( (temp == 0xD9) && (temp_last == 0xFF) ) //If find the end ,break while, break; delayMicroseconds(15); } myCAM.CS_HIGH(); is_header = false; return 1; }

Best regards, ArduCAM support team.

jassi0709 commented 7 years ago

Hello @supprot Thank you again for you help but it was not useful at all.

I've managed to understand the problem of reading speed and the burst mode problem. It was related to a bug in the Crazyflie-firmware deck.

Can you please answer to this. The hostV2 was not useful.

In addition I've seen that entering a different "Resolution mode" (register 0x12 | COM7) from UXGA to CIF or SVGA is quite a challenge. Do you have any example that work on Arduino and put the camera in CIF or SVGA mode in order to achieve higher frame rate?

I'm asking this, because, according to my graphs it looks like that the camera is actually slowing me down. You can see that I spend more time waiting for the next frame compared to the acquisition phase. What can I do? scope_3_comment

Thank you. Best regards, Jaskirat

supprot commented 7 years ago

@jassi0709 Hi, ArduCAM_Host_V2 and our Streamimg demo have our agreement and you can not use it with other demos. In our Streaming demo, after a frame of datas has been processed,it will wait the next frame of datas immediately.However,The time we send start capture singal is not sure which means we may miss some datas. But the lost datas do not exceed one frame.

Let us know if you need more help. Regards, ArduCAM support team.

jassi0709 commented 7 years ago

@supprot Hello, I've pretty much solved the main issue by myself.

I just would like to know why, in your code, you have this particular bit correction: https://github.com/ArduCAM/Arduino/blob/master/ArduCAM/ArduCAM.cpp#L789-L790

I'm asking this because, with the STM32 spi i need to rotate 2 bits and not only one. Can you tell me why this happens, please?

Best regards, Jaskirat

supprot commented 7 years ago

@Jaskirat, Sorry to let you know that the old ArduCAM-Mini-5MP module has SPI timing issue when used in ARM architecture hardware which cause the bit rotation issue. It works without any problem with standard AVR based Arduino boards, we don't know exactly why. But our new ArduCAM-Mini-5MP-Plus has fixed this issue and don't need any addtional operation on SPI.

ReamonSWAT commented 7 years ago

Hi, where can i get the Library of ArduCAM-Mini-5MP for STM32?