OV7670 XCLK Timing/Wiring

MOKDO

New member
Hello, I am currently working on a project that requires camera input for object tracking. I am currently using Teensy 4.0 and the OV7670 camera. I am trying to get the timing down with the XCLK, PCLK, and HREF, but seem to be getting issues mainly from the XCLK. Right now, the only time that the XCLK seems to work is when I touch the wire. I'm assuming that either A: I messed up the wiring somehow, or B: My code is completely dysfunctional. Any advice is greatly appreciated! Also, please explain it to me like I am dumb, I really want to learn more about this, and I, very obviously, lack current skill right now. What I am least comfortable with is probably the PWM, and analogWrite() stuff, I don't exactly understand all that is happening. Thank you!

C++:
// Define OV7670 pin connections
#define VSYNC_PIN 2
#define HREF_PIN 4
#define PCLK_PIN 3
#define XCLK_PIN 5

#define D0_PIN 8
#define D1_PIN 9
#define D2_PIN 10
#define D3_PIN 11
#define D4_PIN 12
#define D5_PIN 13
#define D6_PIN 14
#define D7_PIN 15

int byteNum = 0;
uint8_t byteN = 0;
int highCap = 0;
int lowCap =0;
uint8_t highByte = 0;
uint8_t lowByte = 0;
int HREF_prev = 0;
int PCLK_prev = 0;
bool falling = false;
int currTime = 0;
bool byteLoaded = false;


void setup() {
  // Initialize Serial Communication
  Serial.begin(115200);


  // Initialize OV7670 pins
  pinMode(VSYNC_PIN, INPUT);
  pinMode(HREF_PIN, INPUT);
  pinMode(PCLK_PIN, INPUT);
  pinMode(XCLK_PIN, OUTPUT);
 
  pinMode(D0_PIN, INPUT);
  pinMode(D1_PIN, INPUT);
  pinMode(D2_PIN, INPUT);
  pinMode(D3_PIN, INPUT);
  pinMode(D4_PIN, INPUT);
  pinMode(D5_PIN, INPUT);
  pinMode(D6_PIN, INPUT);
  pinMode(D7_PIN, INPUT);



  attachInterrupt(digitalPinToInterrupt(PCLK_PIN), fallingPCLK, FALLING);
  //attachInterrupt(digitalPinToInterrupt(PCLK_PIN), risingPCLK, RISING);


  //provides the timing for the system clock, we desire a 24 MHz clockrate?
  //PWM divides CPU by 4, so for 24 Mhz, we need the CPU at 528 MHz, which gives us a PWM base of 132 Mhz
   analogWriteFrequency(XCLK_PIN, 24000000); //sets pulse freq to 24 MHz
   //we will write an 128 long pulse with this frequency, this pulse acts as our "high".
   analogWrite(XCLK_PIN, 128); //writes a PWM signal of 128 to XCLK_PIN, giving us a 50% duty cycle
   delay(100);
  attachInterrupt(digitalPinToInterrupt(XCLK_PIN), change, CHANGE);
}


 


void loop() {
  static bool capture = false;
  //interrupts();

  if (digitalRead(VSYNC_PIN) == LOW) {
    // Start of a frame
    capture = true;
   // Serial.println("Start of frame");
  } else if (digitalRead(VSYNC_PIN) == HIGH && capture) {
    // End of a frame
    capture = false;
   // Serial.println("End of frame");
  }

  //Serial.print(digitalRead(HREF_PIN));
  //rising edge of HREF indicates line start, falling edge indicates line end
  if (capture && digitalRead(HREF_PIN) == HIGH) { //this means we are on a line
      //Serial.println("Begin Line");
     

        if(byteNum == 0 && byteLoaded){ //set value for pixel bits 15-8
          byteNum = 1; //set to next byte
          highByte = byteN;
          byteLoaded = false;
          highCap = 1;
        }
        else if(byteNum == 1 && byteLoaded){ //set value for pixel bits 7-0
          byteNum = 0;
          lowByte = byteN;
          byteLoaded = false;
          lowCap = 1;
        }
        if(lowCap != 0 && highCap != 0){
          lowCap = 0;
          highCap = 0;
          uint16_t pixel = 0;
          pixel |= highByte << 8;
          pixel |= lowByte;
          lowByte = 0;
          highByte = 0;
          Serial.println(pixel, HEX);
        }
   
  }




}

uint8_t readByte() {
  uint8_t newByte = 0;
    newByte |= (digitalRead(D0_PIN) << 0);
    newByte |= (digitalRead(D1_PIN) << 1);
    newByte |= (digitalRead(D2_PIN) << 2);
    newByte |= (digitalRead(D3_PIN) << 3);
    newByte |= (digitalRead(D4_PIN) << 4);
    newByte |= (digitalRead(D5_PIN) << 5);
    newByte |= (digitalRead(D6_PIN) << 6);
    newByte |= (digitalRead(D7_PIN) << 7);

  return newByte;
}

void fallingPCLK(){ //falling pclk indicates the start of a new byte, our cycle time is rougly 25 ns
//the first 15 ns, are for the setup, and last 10 ish are for holding, so we need to stall 15 ns.
    //the next rising edge will be when we can read a byte
      //Serial.println("hio");
    delay(0.000015); //15 ns
    if(digitalRead(HREF_PIN) == HIGH){
      byteN = readByte();
      //Serial.println(byteN, HEX);
      byteLoaded = true;
    }
   

}

void change(){
  Serial.println(digitalRead(XCLK_PIN));
}
 
Last edited:
Update:
I think I figured out the main reason why it wasn't working. I had the Teensy wired into an external 3.3v source, while also plugged in via usb. I assume that this messed with some things, though I would appreciate if someone could explain that better to me.
I get a actual clock now which is nice, however the pixel data I'm reading in is mainly just all 1's, which is definitely not right. I assume that this an issue in the timing between XCLK, PCLK, and HREF. From what I understand, is that I am trying to follow this timing diagram.
1724602843684.png

So, while HREF is in a high state, and the cycle after PCLK is falling, I should be looking to read in pixel data. I'm assuming that specifically I should try to read in during t_HD, where I assume the data is fully resolved from the camera. However, it's hard to tell the actual amount of time I have here since I don't know the frequency of PCLK. Again, any help is greatly appreciated!
 
This isn't going to work by using an interrupt to read individual data lines - the Teensy won't be able to complete the interrupt routine before the next pixel arrives. It would be better to use FlexIO to read several pixels at a time and then trigger an interrupt for the CPU to move them to memory.
 
This isn't going to work by using an interrupt to read individual data lines - the Teensy won't be able to complete the interrupt routine before the next pixel arrives. It would be better to use FlexIO to read several pixels at a time and then trigger an interrupt for the CPU to move them to memory.
Thanks for the help! I started looking at some of the FlexIO stuff, and it looks like it will work a lot better!
 
I assume you have a common ground between external power and the teensy...

Also you might take a look at a library that couple of us have been playing with:
I have been messing around with that library for a little while now, and I was having a bit of trouble with it. So I decided to try and just get the absolute minimum going, just so I could understand more of what was happening. Also yes, I have a common ground between them!
 
I have been messing around with that library for a little while now, and I was having a bit of trouble with it. So I decided to try and just get the absolute minimum going, just so I could understand more of what was happening. Also yes, I have a common ground between them!

Sorry, I don't think I have tried doing much with the cameras using the T40 @mjs513 did you try any of these?
This isn't going to work by using an interrupt to read individual data lines - the Teensy won't be able to complete the interrupt routine before the next pixel arrives. It would be better to use FlexIO to read several pixels at a time and then trigger an interrupt for the CPU to move them to memory.
With using FlexIO, it can be a challenge on the T4. I did get some of the parallel displays to work on them, but had to do some trickery.
They do not have 8 consecutive flexio pins...
For the displays we were using:
Code:
#elif defined(ARDUINO_TEENSY40)
// BUGBUG Nibble mode
#define DISPLAY_RD 20    // FlexIO3:10: RD
#define DISPLAY_WR 21    // FlexIO3:11 WR


#define DISPLAY_D0 19     // FlexIO3:0 D0
#define DISPLAY_D1 18     // FlexIO3:1 |
#define DISPLAY_D2 14     // FlexIO3:2 |
#define DISPLAY_D3 15     // FlexIO3:3 |
#define DISPLAY_D4 17     // FlexIO3:6 |
#define DISPLAY_D5 16     // FlexIO3:7 |
#define DISPLAY_D6 22     // FlexIO3:8 |
#define DISPLAY_D7 23     // FlexIO3:9 D7
For the cameras the RD/WR are not there, but instead you probably need pixclk to be flexio.

The code would need use the FlexIO, setup in 10 bit input mode, where there is actually a hole of two bits between two nibbles of data, and you would then need to compress out those two bits...

You setup the master clock using PWM. Where you set the frequency using the code you have:
Code:
analogWriteFrequency(XCLK_PIN, 24000000); //sets pulse freq to 24 MHz
   //we will write an 128 long pulse with this frequency, this pulse acts as our "high".
   analogWrite(XCLK_PIN, 128); //writes a PWM signal of 128 to XCLK_PIN, giving us a 50% duty cycle
   delay(100);
  attachInterrupt(digitalPinToInterrupt(XCLK_PIN), change, CHANGE);

Need to run...
 
Sorry, I don't think I have tried doing much with the cameras using the T40 @mjs513 did you try any of these?
I am not sure we ever tried using the T4.0. Think we did the testing on the T4.1 and Teensy Micromod. Its been quite a while since I played the OV7670. Think we went over to the t41 and Tmm because of the memory since we started supporting higher resolutions cameras.
 
You can use shift registers 0 and 1 in logic mode to "clone" input signals on FlexIO3 D2 to D4 and D3 to D5. So that gives you 8 consecutive input signals on D4-D11 (logic modes update asynchronously so all signals would be in sync). That leaves D0 and D1 for hsync and pix_clk, vsync can go on any GPIO since it's not particularly important for capturing data.
 
Back
Top