Problem trying to read OV7670 camera under IRQ Teensy 4.0

Status
Not open for further replies.
I've tuned up my TA4.1 code and my PC image host program to speed up transfer of images from the PSRam image buffer to the PC. In fact, it seems to be working impossibly well! I'm getting a transfer rate of about 23MB/second. That seems impossibly fast when the PSRAM SCK is 88MHz. That clock would seem to limit the SPI transfer to about 11MB/second.

ImageHost3.jpg

Here's the code for the T4.1 part of the transfer:
Code:
void SendPacket(uint8_t *bptr, uint32_t imagesize){
  uint16_t num2send;

  uint32_t  bytesleft, uptotal;
  cameraPause();
  uptotal = 0;

  do {
    bytesleft = imagesize - uptotal;
    if (bytesleft > 8192) {
      num2send = 8192;
    } else {
      num2send = bytesleft;
    }
    uptotal += num2send;
    Serial.write(bptr, num2send);
    IMARKHI  // for scope probing on pin 32
    delayMicroseconds(50);
    bptr += num2send;
    IMARKLO
  } while (uptotal < imagesize); // end of do loop
}


and here's the PC receiver routine:
Code:
	readptr = (unsigned char *)&cambuff;
	pktrcvd = 0;
	errcount = 0;
	pktdone = false;
	do {
		numread = ReadCommData(readptr, 8192);
		pktrcvd += numread;
		readptr += numread;
		if(numread == 0){
			errcount++;  // keep track of RCV stalls
			DelayUsec(200);  // wait a while for more input
			if(errcount > 2000) pktrcvd = pktsize;
		}
		if(pktrcvd >= pktsize)pktdone = true;
	} while (!pktdone);

The receive stalls occur when the PC requests an 8KB block but there are no bytes available in the PC receive queue.

How is the T4.1 able to send data from PSRAM at 23MB/Second? One possibility is that the USB transfers are really just a chain of DMA requests, which get queued up in the USB driver, and the actual transmission continues well after the end of the packet transfer function. I'll check that possibility by doing some timing checks on the PC end.
 
DOH! (Slapping self upside the head!) So in this application, it should be able to crank right along as it is reading linearly increasing contiguous bytes. The PSRam should only require an initial address input at the start of the transfer then just keep on cranking out data 4 bits at a time for the next 614400 bytes of the VGA frame. Now that I have that puzzle solved, I can proceed with the implementation of the SCI interface code.
 
I've tuned up my TA4.1 code and my PC image host program to speed up transfer of images from the PSRam image buffer to the PC. In fact, it seems to be working impossibly well! I'm getting a transfer rate of about 23MB/second. That seems impossibly fast when the PSRAM SCK is 88MHz. That clock would seem to limit the SPI transfer to about 11MB/second.

...

Good work ...

>> Not hooked one up yet or following your code ... but the 32KB cache works across the PSRAM, maybe it is helping as that will transfer at CPU speed if it is covering the data

I posted this Oct 17 on an emulator thread:
Code:
...PSRAM access speed setup changed from 88 MHz to 132MHz?  If that is a bottleneck it might be some easy help to performance.

link here : [URL="https://forum.pjrc.com/threads/62456-Teensy-4-1-PSRAM-Random-Access-Latency?p=249444&viewfull=1#post249444"]Teensy-4-1-PSRAM-Random-Access-Latency[/URL]
 
DOH! (Slapping self upside the head!) So in this application, it should be able to crank right along as it is reading linearly increasing contiguous bytes. The PSRam should only require an initial address input at the start of the transfer then just keep on cranking out data 4 bits at a time for the next 614400 bytes of the VGA frame. Now that I have that puzzle solved, I can proceed with the implementation of the SCI interface code.
If you get a chance, would you mind posting more of the code that shows your current setup,

I have made some progress with the DMA code, but so far having it work cleanly is sort of a pain. So using the CSI interface probably makes a lot more sense!

Again looks like yours is working pretty well!
 
@mborgerson - yes please - wiring too ... what @KurtE said. I have a pair of cameras and could build one to a T_4.1 to play along - but too many wires to do and redo while testing against evolving code.
 
I am guessing it is still based off of the stuff up on: https://github.com/braingram/teensy_tcm8230md

Where I believe he has some of the pins defined as:
Code:
// CSI_MCLK AD_B1_05
#define ECLK_PIN 41
#define RST_PIN 15

#define SCL_PIN 19
#define SDA_PIN 18

// CSI_VSYNC AD_B1_06
#define VD_PIN 17
// CSI_HSYNC AD_B1_07
#define HD_PIN 16
// CSI_PIXCLK AD_B1_04
#define DCLK_PIN 40

// CSI_DATA0X...
// AD_B1_15
#define D0_PIN 27
// AD_B1_14
#define D1_PIN 26
// AD_B1_13
#define D2_PIN 39
// AD_B1_12
#define D3_PIN 38
// AD_B1_11
#define D4_PIN 21
// AD_B1_10
#define D5_PIN 20
// AD_B1_09
#define D6_PIN 23
// AD_B1_08
#define D7_PIN 22

Note: Recently updated my excel document again to clean up some of the CSI marking, which you can see here:
T4.1-Cardlike.jpg
They are sort of hung out in no mans land (in prop shield columns), but with a light green background.
Some of the signals will be obvious which pin used as only possible pin, But looks like choice for PIXCLK and VSYNC.

So looking at the code: I see:
Code:
// VSYNC
  IOMUXC_SW_MUX_CTL_PAD_GPIO_AD_B1_06 = 0x4U;
  IOMUXC_CSI_VSYNC_SELECT_INPUT = 0x1U;
  IOMUXC_SW_PAD_CTL_PAD_GPIO_AD_B1_06 = 0x0U;
  // HSYNC
  IOMUXC_SW_MUX_CTL_PAD_GPIO_AD_B1_07 = 0x4U;
  IOMUXC_CSI_HSYNC_SELECT_INPUT = 0x1U;
  IOMUXC_SW_PAD_CTL_PAD_GPIO_AD_B1_07 = 0x0U;
  // DCLK
  IOMUXC_SW_MUX_CTL_PAD_GPIO_AD_B1_04 = 0x4U;
  IOMUXC_CSI_PIXCLK_SELECT_INPUT = 0x0U;
  IOMUXC_SW_PAD_CTL_PAD_GPIO_AD_B1_04 = 0x0U;

So looks like:
VSYNC on 17
HSYNC on 16
PIXCLK on 40
MCLK on 41
D2-D9 as above Marked D0-D7 as 8 bit devices use CSI_D2-CSI_D9

I may wire up the 2nd camera to a T4.1 like this and who knows maybe with an ILI9488 or HX8357 for larger view area.
Maybe fun to create a board ;)

EDIT - I should note that the github project I mentioned in this post was for a different camera, which is why it would be great to see your version for this camera.
 
Last edited:
I'm about 90% of the way to getting the T4.1 to read the OV7670 using the CSI. It's pretty neat in that it just cranks away under the hood, collecting buffers of VGA images using DMA. You don't even have to enable the CSI interrupts. I should have some cleaned-up code and a wiring diagram to post tomorrow. Of course, finishing up that last 10% of the code will take the second half of the time!

I'm still having some problems with rolling and improperly framed images. I think these are related to mismatches between the register settings of the OV7670 and the settings of the CSI. There are a lot of combinations of horizontal and vertical sync signal polarity that have to be matched between the camera and the CSI. Since I'm getting reasonable, if misframed, images, I think all the hardware parts are working OK. I can get images at pixel clocks up to about 12MHz, so it may be possible to get 15 FPS or more.
 
That would be great.

Today I hacked up my other version using GPIO input. Still having issues with continuous DMA

But I do have a continuous output version using ILI9341 display using some pretty simple code...

Once I enter the "s" into command window to start continuous mode it runs the code:
Code:
  if (g_continuous_mode) {
    Camera.readFrame(pixels);
    int numPixels = Camera.width() * Camera.height();

    for (int i = 0; i < numPixels; i++) pixels[i] = (pixels[i] >> 8) | (((pixels[i] & 0xff) << 8));
    tft.waitUpdateAsyncComplete();  // wait while any other is pending...
    tft.fillScreen(ILI9341_BLACK);
    tft.writeRect(ILI9341_t3n::CENTER, ILI9341_t3n::CENTER, Camera.width(), Camera.height(), pixels);
    tft.updateScreenAsync(); // start an async update...
    g_count_frames_output++;
    if (g_emCycles >= 1000) {
      Serial.print("Cycles Per second: ");
      Serial.println((float)(g_count_frames_output * 1000.0) / (float)g_emCycles, 2);
//    Serial.printf("Cycle time: %lu\n", (uint32_t)g_emCycles);
      g_emCycles = 0;
      g_count_frames_output = 0;
    }
  }
So it simply reads in the next frame, I wait for any active updating of the display to complete, I clear the screen and writerect. I can remove the fillScreen as I am doing full screen...
And every second I get an estimate of how many frames per second output:

And it runs pretty nicely:
Code:
Send the 'c' character to read a frame ...
Send the 'd' character to read a frame using DMA ...
Send the 's' character to start/stop continuous display mode

*** Continuous mode turned on
Cycles Per second: 18.96
Cycles Per second: 20.83
Cycles Per second: 20.83
Cycles Per second: 20.85
Cycles Per second: 20.83
Cycles Per second: 20.85
Cycles Per second: 20.83
Cycles Per second: 20.83
Cycles Per second: 20.85
Cycles Per second: 20.83
Cycles Per second: 20.85
Cycles Per second: 20.83
Cycles Per second: 19.84
Cycles Per second: 20.85
Cycles Per second: 20.83
Cycles Per second: 20.85
Cycles Per second: 20.83
Cycles Per second: 20.83
Cycles Per second: 20.85
Cycles Per second: 20.83
Cycles Per second: 20.85
Cycles Per second: 20.83
Cycles Per second: 20.83
Cycles Per second: 20.85
Cycles Per second: 20.83
*** Continuous mode turned off
 
OK, I've finally got the T4.1 and the OV7670 talking nicely. I spent most of the day tracking down one misconfigured register in the CSI. I kept having problems with the picture wandering around in the window. I finally figured it out (I hope) and will clean up the code and post it tomorrow. Tonight you get the wiring diagram and some pictures of my wired board.

T4_1 OV7670 wiring.png
You may recognize the Excel document on which this is based! (Thanks, KurtE)

Here are the front and back of my soldered breadboard.
Cam Proto Top.jpg

Cam_Proto Bottom.jpg

The sockets on the top of the board are LIF (Low Insertion Force) versions left over from interface boards I sold a decade or so ago. They are dual-row and the outside row is available for plug in wires for 'scope probes, etc.
The labels on the bottom are to compensate for senior moments where I can't remember which wire goes to which pin. I used Kynar wire-wrap wire that was probably purchased sometime late in the last century. The wire is silver coated and has survived in good shape for soldering.
 
Nice details - will look to replicate the wiring ...

<edit>
Pictures printed - trying to see the best way to solder/wire all that to an upgraded T_4.1 - may attempt in a few hours ... that PCB looks like one here - and wire-wrap wire here much newer. Bottom labels seem a good add too for rotationally/flipped challenged.

Just saw two hidden/undocumented pullups on i2c lines SDA & SCL - can't make out the values?

@KurtE will hopefully comment by then to confirm all looks well ... The edited KurtE table matches the sub-tables okay and seems all accounted for - only PCLK/#40 was not function name matched.
 
Last edited:
Workable CSI code

I just noted that on my capture of the Excel sheet, I had CSI_PIXCLK on pin 35-----which will work if you adjust the IOMux setup. However. after I captured that picture, I decided to move PIXCLK to pin 40, which is the setup on the table to the right and on the board itself.

I've attached zip file that includes the CSI setup and the camera register setup code. I'm holding on to my sketch that calls the CSI and camera functions for now, as it uses a command processing library and sends the buffer output as a single Serial.write(cambuff, 614400) burst. If you try that with a normal comms program, you'll probably swamp your PC USB input handler. It will work with a program that knows to halt screen updates and simply buffer the USB input for later display--but distributing PC Executables is not easy if the receiver is worried about malware, so I'm still figuring out what to do with my ImageHost.exe program.

View attachment OV7670_MB.zip

Now that I've got the CSI working, I plan to convert my code to library format so that you can add image collection by instantiating and OV7760 object and calling its member functions. Among the member functions would be the ability to capture an image to a user-defined buffer with a prepended header that would make it look like a PC .BMP file. You could then write the .BMP file to the SD card for later viewing on a PC. You could also have a sketch that implements the MTP protocol to allow you to open the .BMP file from a PC program like Paint.

I plan to move my posts on the progress of this library to the Project Guidance forum as I consider that the library generation won't involve so much examination of the hardware details of the OV7670 and CSI. It will be more of an exploration of user interface issues. I'll start posting library code when I get a set of member functions and a demo program that can be the basis of your own program to collect and use images from the OV7670. I expect the first thing to do would be to add an interface to an LCD display. What would be your recommendations on a display to use?

Thanks to the OP Cyrille as well as Defragster, KurtE, wagnick, and the others who have kept me occupied and socially distanced for the last month or so.

Now, is anyone ready to dive into the PixelPipeline?
 
I just noted that on my capture of the Excel sheet, I had CSI_PIXCLK on pin 35-----which will work if you adjust the IOMux setup. However. after I captured that picture, I decided to move PIXCLK to pin 40, which is the setup on the table to the right and on the board itself.

I've attached zip file that includes the CSI setup and the camera register setup code. I'm holding on to my sketch that calls the CSI and camera functions for now, as it uses a command processing library and sends the buffer output as a single Serial.write(cambuff, 614400) burst. If you try that with a normal comms program, you'll probably swamp your PC USB input handler. It will work with a program that knows to halt screen updates and simply buffer the USB input for later display--but distributing PC Executables is not easy if the receiver is worried about malware, so I'm still figuring out what to do with my ImageHost.exe program.

View attachment 22214

Now that I've got the CSI working, I plan to convert my code to library format so that you can add image collection by instantiating and OV7760 object and calling its member functions. Among the member functions would be the ability to capture an image to a user-defined buffer with a prepended header that would make it look like a PC .BMP file. You could then write the .BMP file to the SD card for later viewing on a PC. You could also have a sketch that implements the MTP protocol to allow you to open the .BMP file from a PC program like Paint.

I plan to move my posts on the progress of this library to the Project Guidance forum as I consider that the library generation won't involve so much examination of the hardware details of the OV7670 and CSI. It will be more of an exploration of user interface issues. I'll start posting library code when I get a set of member functions and a demo program that can be the basis of your own program to collect and use images from the OV7670. I expect the first thing to do would be to add an interface to an LCD display. What would be your recommendations on a display to use?

Thanks to the OP Cyrille as well as Defragster, KurtE, wagnick, and the others who have kept me occupied and socially distanced for the last month or so.

Now, is anyone ready to dive into the PixelPipeline?

Thanks,

Will be taking look. Yep I did find it does not build but should have all of the pieces to adapt some of the other code to.

Should be fun! Assuming it works well, may have to make a board to hold one with some display. Not sure which display yet. Right now playing with ILI9488, but it is so much slower than ILI9341 due to us having to send about:
(480*320*3)/(320*240*2) = 3 times as many bytes to the screen... Plus having the camera read in 4 times as many pixels... There may be other scale possible of camera, but document mainly show VGA, QVGA, QQVGA, CIF.
May have to experiment.
 
@KurtE

Think what you are looking for is in section 6 of the Implementation Guide on Image Scaling!
 
@KurtE

Think what you are looking for is in section 6 of the Implementation Guide on Image Scaling!

Yes - But for the most part it is geared toward VGA, QVGA... as shown in table 2-2.

It sort of eludes toward other scaling. But the part that may be of interest may be the start/stop settings so for example image scaling for 640x480, but can maybe only return the 480x320. Not sure yet... Probably need to walk before trying to run :D
 
I hope people don't mind that I always take a few diversions ;)

I prefer not having to code like:
Code:
  CSI_CSICR3 =  (1 << 15) | // clear the frame counter
                (1 << 12);//  Enable RxFIFO DMA request

Much rather have:
Code:
  CSI_CSICR3 =  CSI_CSICR3_FRMCNT_RST | // clear the frame counter
                CSI_CSICR3_DMA_REQ_EN_RFF;//  Enable RxFIFO DMA request

So like some of the other subsystems I went through the manual and extracted each of the field names (even when misspelled) and created a PR for Core:
https://github.com/PaulStoffregen/cores/pull/496

Temporarily in code will add these as well with #ifndef
Code:
#ifndef CSI_CSICR1_SWAP16_EN // in process of adding to IMXRT.H
#define CSI_CSICR1_SWAP16_EN                    ((uint32_t)(1<<31))
#define CSI_CSICR1_EXT_VSYNC                    ((uint32_t)(1<<30))
#define CSI_CSICR1_EOF_INT_EN                   ((uint32_t)(1<<29))
#define CSI_CSICR1_PrP_IF_EN                    ((uint32_t)(1<<28))
#define CSI_CSICR1_CCIR_MODE                    ((uint32_t)(1<<27))
#define CSI_CSICR1_COF_INT_EN                   ((uint32_t)(1<<26))
#define CSI_CSICR1_SF_OR_INTEN                  ((uint32_t)(1<<25))
#define CSI_CSICR1_RF_OR_INTEN                  ((uint32_t)(1<<24))
#define CSI_CSICR1_SFF_DMA_DONE_INTEN           ((uint32_t)(1<<22))
#define CSI_CSICR1_STATFF_INTEN                 ((uint32_t)(1<<21))
#define CSI_CSICR1_FB2_DMA_DONE_INTEN           ((uint32_t)(1<<20))
#define CSI_CSICR1_FB1_DMA_DONE_INTEN           ((uint32_t)(1<<19))
#define CSI_CSICR1_RXFF_INTEN                   ((uint32_t)(1<<18))
#define CSI_CSICR1_SOF_POL                      ((uint32_t)(1<<17))
#define CSI_CSICR1_SOF_INTEN                    ((uint32_t)(1<<16))
#define CSI_CSICR1_HSYNC_POL                    ((uint32_t)(1<<11))
#define CSI_CSICR1_CCIR_EN                      ((uint32_t)(1<<10))
#define CSI_CSICR1_FCC                          ((uint32_t)(1<<8))
#define CSI_CSICR1_PACK_DIR                     ((uint32_t)(1<<7))
#define CSI_CSICR1_CLR_STATFIFO                 ((uint32_t)(1<<6))
#define CSI_CSICR1_CLR_RXFIFO                   ((uint32_t)(1<<5))
#define CSI_CSICR1_GCLK_MODE                    ((uint32_t)(1<<4))
#define CSI_CSICR1_INV_DATA                     ((uint32_t)(1<<3))
#define CSI_CSICR1_INV_PCLK                     ((uint32_t)(1<<2))
#define CSI_CSICR1_REDGE                        ((uint32_t)(1<<1))
#define CSI_CSICR1_PIXEL_BIT                    ((uint32_t)(1<<0))

#define CSI_CSICR2_DMA_BURST_TYPE_RFF(n)        ((uint32_t)(((n) & 0x3)<<30))
#define CSI_CSICR2_DMA_BURST_TYPE_SFF(n)        ((uint32_t)(((n) & 0x3)<<28))
#define CSI_CSICR2_DRM                          ((uint32_t)(1<<26))
#define CSI_CSICR2_AFS(n)                       ((uint32_t)(((n) & 0x3)<<24))
#define CSI_CSICR2_SCE                          ((uint32_t)(1<<23))
#define CSI_CSICR2_BTS(n)                       ((uint32_t)(((n) & 0x3)<<19))
#define CSI_CSICR2_LVRM(n)                      ((uint32_t)(((n) & 0x7)<<16))
#define CSI_CSICR2_VSC(n)                       ((uint32_t)(((n) & 0xff)<<8))
#define CSI_CSICR2_HSC(n)                       ((uint32_t)(((n) & 0xff)<<0))
    
#define CSI_CSICR3_FRMCNT(n)                    ((uint32_t)(((n) & 0xffff)<<16))
#define CSI_CSICR3_FRMCNT_RST                   ((uint32_t)(1<<15))
#define CSI_CSICR3_DMA_REFLASH_RFF              ((uint32_t)(1<<14))
#define CSI_CSICR3_DMA_REFLASH_SFF              ((uint32_t)(1<<13))
#define CSI_CSICR3_DMA_REQ_EN_RFF               ((uint32_t)(1<<12))
#define CSI_CSICR3_DMA_REQ_EN_SFF               ((uint32_t)(1<<11))
#define CSI_CSICR3_STATFF_LEVEL(n)              ((uint32_t)(((n) & 0x7)<<8))
#define CSI_CSICR3_HRESP_ERR_EN                 ((uint32_t)(1<<7))
#define CSI_CSICR3_RxFF_LEVEL(n)                ((uint32_t)(((n) & 0x7)<<4))
#define CSI_CSICR3_TWO_8BIT_SENSOR              ((uint32_t)(1<<3))
#define CSI_CSICR3_ZERO_PACK_EN                 ((uint32_t)(1<<2))
#define CSI_CSICR3_ECC_INT_EN                   ((uint32_t)(1<<1))
#define CSI_CSICR3_ECC_AUTO_EN                  ((uint32_t)(1<<0))

#define CSI_CSISR_BASEADDR_CHHANGE_ERROR        ((uint32_t)(1<<28))
#define CSI_CSISR_DMA_FIELD0_DONE               ((uint32_t)(1<<27))
#define CSI_CSISR_DMA_FIELD1_DONE               ((uint32_t)(1<<26))
#define CSI_CSISR_SF_OR_INT                     ((uint32_t)(1<<25))
#define CSI_CSISR_RF_OR_INT                     ((uint32_t)(1<<24))
#define CSI_CSISR_DMA_TSF_DONE_SFF              ((uint32_t)(1<<22))
#define CSI_CSISR_STATFF_INT                    ((uint32_t)(1<<21))
#define CSI_CSISR_DMA_TSF_DONE_FB2              ((uint32_t)(1<<20))
#define CSI_CSISR_DMA_TSF_DONE_FB1              ((uint32_t)(1<<19))
#define CSI_CSISR_RxFF_INT                      ((uint32_t)(1<<18))
#define CSI_CSISR_EOF_INT                       ((uint32_t)(1<<17))
#define CSI_CSISR_SOF_INT                       ((uint32_t)(1<<16))
#define CSI_CSISR_F2_INT                        ((uint32_t)(1<<15))
#define CSI_CSISR_F1_INT                        ((uint32_t)(1<<14))
#define CSI_CSISR_COF_INT                       ((uint32_t)(1<<13))
#define CSI_CSISR_HRESP_ERR_INT                 ((uint32_t)(1<<7))
#define CSI_CSISR_ECC_INT                       ((uint32_t)(1<<1))
#define CSI_CSISR_DRDY                          ((uint32_t)(1<<0))

#define CSI_CSICR3_CSI_ENABLE                   ((uint32_t)(1<<31))
#define CSI_CSICR18_MASK_OPTION(n)              ((uint32_t)(((n) & 0x3)<<18))
#define CSI_CSICR18_AHB_HPROT(n)                ((uint32_t)(((n) & 0xf)<<12))

#define CSI_CSICR18_RGB888A_FORMAT_SEL          ((uint32_t)(1<<10))
#define CSI_CSICR18_BASEADDR_CHANGE_ERROR_IE    ((uint32_t)(1<<9))
#define CSI_CSICR18_LAST_DMA_REQ_SEL            ((uint32_t)(1<<8))
#define CSI_CSICR18_DMA_FIELD1_DONE_IE          ((uint32_t)(1<<7))
#define CSI_CSICR18_FIELD0_DONE_IE              ((uint32_t)(1<<6))
#define CSI_CSICR18_BASEADDR_SWITCH_SEL         ((uint32_t)(1<<5))
#define CSI_CSICR18_BASEADDR_SWITCH_EN          ((uint32_t)(1<<4))
#define CSI_CSICR18_PARALLEL24_EN               ((uint32_t)(1<<3))
#define CSI_CSICR18_DEINTERLACE_EN              ((uint32_t)(1<<2))

#define CSI_CSICR19_DMA_RFIFO_HIGHEST_FIFO_LEVEL(n) ((uint32_t)(((n) & 0xf)<<12))
#endif
Obviously let me know if you see some place I screwed up
 
Thanks,

Will be taking look. Yep I did find it does not build but should have all of the pieces to adapt some of the other code to.


Sorry about that. When I posted the CSI and Camera stuff, I forgot that those elements referred to some global variables in the interrupt routine for the CSI.

This simple front end should build and allow you to verify that the code is functioning:

Code:
//  t4.1 PIN input  and image packet test
//  New CameraBB with new pinouts for wired breadboard
//  MJB  10/27/2020

#include <stdint.h>
#include <Wire.h>

#define FRAME_WIDTH 640l
#define FRAME_HEIGHT 480l

#define FB_WIDTH 640l
#define FB_HEIGHT 480l
#define FB_COUNT 2  // two frames in EXTMEM

#define FRAMEBYTES (FRAME_WIDTH * FRAME_HEIGHT *2)
#define FRAMEBUFFBYTES (FB_WIDTH *FB_HEIGHT *2)

uint8_t fb1[FRAMEBUFFBYTES] EXTMEM;
uint8_t fb2[FRAMEBUFFBYTES] EXTMEM;

uint32_t masterfreq = 24;
volatile uint32_t last_csistat, csiIrqCount;
volatile uint32_t fb1count, fb2count;
volatile uint32_t bswitcherrors;
uint32_t framecount;


const char compileTime [] = " Compiled on " __DATE__ " " __TIME__;

const int ledpin    = 13;
const int imarkpin = 32;
const int pinCamReset = 14;


#define  LEDON digitalWriteFast(ledpin, HIGH); // Also marks IRQ handler timing
#define  LEDOFF digitalWriteFast(ledpin, LOW);
#define  LEDTOGGLE digitalToggleFast(ledpin);

#define  IMARKHI digitalWriteFast(imarkpin, HIGH); // Also marks IRQ handler timing
#define  IMARKLO digitalWriteFast(imarkpin, LOW);
#define  IMARKTOGGLE digitalToggleFast(imarkpin);

void setup() {
  Serial.begin(9600);
  delay(200);
  Wire.begin();

  delay(100);
  pinMode(ledpin, OUTPUT);
  pinMode(imarkpin, OUTPUT);
  pinMode(pinCamReset, OUTPUT);

  delay(10);
  memset(&fb1, 0, FRAMEBUFFBYTES * 2);

  digitalWriteFast(pinCamReset, LOW);
  delay(10);
  digitalWriteFast(pinCamReset, HIGH);  // subsequent resets via SCB


  Serial.printf("\n\nOV7670 Camera Test 3 %s\n", compileTime);
  cameraBegin(640, 480, fb1, fb2);

}

void loop() {
  static uint32_t lastfc;
  static uint32_t fc = 0;
  char ch;
  if (Serial.available()) {
    ch = Serial.read();
    if (ch == 's') CMSI();
    if (ch == 'c') CMCS();
    if (ch == 'r') CMCR();
    if (ch == 'f') CMGF();
  }

  //  This extends frame count from 16 to 32 bits
  fc  = CSI_CSICR3 >> 16; // get the 16-bit frame count
 
  framecount += (fc - lastfc);
  lastfc = fc;
}

// Show information
void CMSI(void) {

  Serial.printf("\n\nOV7670 Camera Test 3 %s\n", compileTime);
  Serial.printf("buff1 at %p   buff2 at %p\n", &fb1, &fb2);
  Serial.printf("Camera frame count = %lu \n", framecount);
  Serial.printf("Base Address Change errors: %lu\n", bswitcherrors);
  Serial.printf("csistat: %08X, csiIrqCount: %lu\n", CSI_CSISR, csiIrqCount);
  Serial.printf("fb1 count: %6lu   fb2 count %6lu\n",fb1count,fb2count);
}

void CMCS(void) {  // Show CSI registers
  print_csi_registers();
}



// Show Camera Registers
void CMCR(void) {
  uint8_t regs[200];
  cameraReadAll(regs);
  cameraShowAll(regs);
}


// Get a frame of data
void CMGF(void) {
  uint32_t imagesize, fbc;
  imagesize = (FRAME_WIDTH * FRAME_HEIGHT * 2);
  fbc = fb2count;
  while (fbc == fb2count); // wait until a new frame in fb2 is finished
  // Don't try sending unless your program can handle 614400 bytes in a hurry!
  //Serial.write(fb2,magesize);
  Serial.printf("fb2 ready for processing %lu nytes.", imagesize);
}

Start a sketch with this code and add the contents of the zip file to the sketch folder.
 
Thanks, the sketch compiles and I am getting some debug information :D
Pin numbers matched I think the ones I used.

Code:
OV7670 Camera Test 3  Compiled on Oct 27 2020 10:23:50
cam clock  12.00 with pll=1 and div=1
After cameraBegin(end setup)


OV7670 Camera Test 3  Compiled on Oct 27 2020 10:23:50
buff1 at 0x70096000   buff2 at 0x70000000
Camera frame count = 553 
Base Address Change errors: 0
csistat: 8E224001, csiIrqCount: 554
fb1 count:    276   fb2 count    276
==== CSI ====
CSI_CSICR1: 401B0912
CSI_CSICR2: C0000000
CSI_CSICR3: 3621020
CSI_CSISTATFIFO: 0
CSI_CSIRFIFO: 83CC83AC
CSI_CSIRXCNT: 9600
CSI_CSISR: 80204000
CSI_CSIDMASA_STATFIFO: 0
CSI_CSIDMATS_STATFIFO: 0
CSI_CSIDMASA_FB1: 70096000
CSI_CSIDMASA_FB2: 70000000
CSI_CSIFBUF_PARA: 0
CSI_CSIIMAG_PARA: 50001E0
CSI_CSICR18: 800AD210
CSI_CSICR19: 10
CSI_CSIRFIFO: 83CC83AC
fb2 ready for processing 614400 nytes
Note to Paul - Still getting double lines on output to terminal monitor -CRLF double spaces (i.e. Serial.println())

Next up: probably add a few more prints... At first I did not think anything was working as nothing printed out...
So added the one print... May add a couple others to remind me what the letter commands are.

Will probably hook up to TFT display to see image... Not sure which one yet. ILI9488 I already have code to display part of frame from larger image.
Could go to RA8875 800x480... or ???

Plus may play with the register field stuff like I mentioned.

Thanks again!
 
Afternoon all

Ok using posts by @mborgerson and hooked up the OV7670 to the T4.1. Looks like I am getting responses on the serial monitor but not sure what to used to view the image on the PC?

EDIT _ BTW what should the data look like coming out of the camera using the commands?
 
@KurtE - looks like you answered my question :)

Looks like I have a connection problem:
Code:
OV7670 Camera Test 3  Compiled on Oct 27 2020 13:45:22
buff1 at 0x70096000   buff2 at 0x70000000
Camera frame count = 42562 
Base Address Change errors: 32545
csistat: 8E224001, csiIrqCount: 42563
fb1 count:  12024   fb2 count  12024
==== CSI ====

CSI_CSICR1: 401B0912

CSI_CSICR2: C0000000

CSI_CSICR3: A7531020

CSI_CSISTATFIFO: 681561B8

CSI_CSIRFIFO: 31551055

CSI_CSIRXCNT: 9600

CSI_CSISR: 86204001

CSI_CSIDMASA_STATFIFO: 0

CSI_CSIDMATS_STATFIFO: 0

CSI_CSIDMASA_FB1: 70096000

CSI_CSIDMASA_FB2: 70000000

CSI_CSIFBUF_PARA: 0

CSI_CSIIMAG_PARA: 50001E0

CSI_CSICR18: 800AD210

CSI_CSICR19: 10

CSI_CSIRFIFO: 30655065

fb2 ready for processing 614400 nytes
 
Warning the files show two different hook ups. The wrong one in OV7670_MB
Code:
  const int PinCamVsync = 34; //
  const int PinCamHref  = 16; //
  const int PinCamPpclk = 35; //
  const int PinCamXclk  = 41; //
  const int PinCamD0    = 27; // AD_B1_15
  const int PinCamD1    = 26; //
  const int PinCamD2    = 39; //
  const int PinCamD3    = 38; //
  const int PinCamD4    = 21; //
  const int PinCamD5    = 20; //
  const int PinCamD6    = 23; //
  const int PinCamD7    = 22; // AD_B1_8

The right one in: CSI_41

Code:
#define ECLK_PIN 41// CSI_MCLK AD_B1_05
[COLOR="#FF0000"]#define VD_PIN 17 //CSI_VSYNC AD_B1_06[/COLOR]
#define HD_PIN 16 // CSI_HSYNC AD_B1_07
[COLOR="#FF0000"]#define DCLK_PIN 40 // CSI_PIXCLK AD_B1_04[/COLOR]

#define D0_PIN 27// AD_B1_15
#define D1_PIN 26// AD_B1_14
#define D2_PIN 39// AD_B1_13
#define D3_PIN 38// AD_B1_12
#define D4_PIN 21// AD_B1_11
#define D5_PIN 20// AD_B1_10
#define D6_PIN 23// AD_B1_09
#define D7_PIN 22// AD_B1_08

That is there are two valid VSYNC pins 34 and 17 Mine is 17
Likewise for PPCLK 35 and 40, mine is 40

I am assuming that the data should be straight 640x480 frame image with 565 format..
 
Warning the files show two different hook ups. The wrong one in OV7670_MB
Code:
  const int PinCamVsync = 34; //
  const int PinCamHref  = 16; //
  const int PinCamPpclk = 35; //
  const int PinCamXclk  = 41; //
  const int PinCamD0    = 27; // AD_B1_15
  const int PinCamD1    = 26; //
  const int PinCamD2    = 39; //
  const int PinCamD3    = 38; //
  const int PinCamD4    = 21; //
  const int PinCamD5    = 20; //
  const int PinCamD6    = 23; //
  const int PinCamD7    = 22; // AD_B1_8

The right one in: CSI_41

Code:
#define ECLK_PIN 41// CSI_MCLK AD_B1_05
[COLOR="#FF0000"]#define VD_PIN 17 //CSI_VSYNC AD_B1_06[/COLOR]
#define HD_PIN 16 // CSI_HSYNC AD_B1_07
[COLOR="#FF0000"]#define DCLK_PIN 40 // CSI_PIXCLK AD_B1_04[/COLOR]

#define D0_PIN 27// AD_B1_15
#define D1_PIN 26// AD_B1_14
#define D2_PIN 39// AD_B1_13
#define D3_PIN 38// AD_B1_12
#define D4_PIN 21// AD_B1_11
#define D5_PIN 20// AD_B1_10
#define D6_PIN 23// AD_B1_09
#define D7_PIN 22// AD_B1_08

That is there are two valid VSYNC pins 34 and 17 Mine is 17
Likewise for PPCLK 35 and 40, mine is 40

I am assuming that the data should be straight 640x480 frame image with 565 format..

I think the pin definitions in OV7670_MB are commented out. They should have been removed as they are not used.

The output is an 640 x 480 array of 16-bit RGB565 values. My PC host converts that frame to an array of RGB888 values for PC display. As I said, distributing PC executables is a questionable thing, so I'm thinking about code to write files to the SD in .bmp format.

It would be interesting to see if a Python program using PySerial could capture a transfer from the T4.1. That would solve the executable distribution problem and result in a nice cross-platform option. Alas, my Python skills are still at the "Hello World" stage.
 
Status
Not open for further replies.
Back
Top