OV7670 camera recording

I have (hopefully) several collaborations coming up next year !

  • Circuit bending digital cameras with the Base Photo at the Beaux-Arts
  • Solar-powered sound/light/kinetic outdoor sculptures with the Art en situation filliere at the Beaux-Arts
  • Voice synthesis with P5js and hopefully in hardware too

****

Check out this debug tool for the Play Station which shows the VRAM (from https://www.copetti.org/writings/consoles/playstation/):

There is a double screen buffer on the left and on the right are CLUTs and textures. Here’s a map showing how the VRAM is divided :

Also interesting that the Play Station has a chip to quickly decompress images stored in a custom format. Could I make my own kind of simple Run length encoding compression ?

Looking at sprites there are also flipbooks, collections of sprites that can be used to form animations (such as a candle flickering).

Could I experiment with applying textures to zones ? Could I make “functions” in verliog to draw lines, triangles and rectangles ?

*****

Some things to try :

A color LUT which can be randomly generated and regenerated at the push of a button. With four bits for each color, I have 12 bit depth and would need 4096-1 entries in the table (?). I am following this tutorial https://projectf.io/posts/display-signals/ and using this CLUT https://github.com/projf/projf-explore/blob/main/lib/res/palettes/antiquity16_4b.mem.

I had a tough time getting IceCube2 to accept this idea. Hard to instantiate memory, though having reset trigger loading memory could have been an option. I went for an 16 element array of 12 bit values like this :

 //CLUT
reg [11:0] CLUT[0:15] =

{
12'h222,
12'h322,
12'h432,
12'h743,
12'hB75,
12'hFA7,
12'hFC8,
12'h675,
12'h995,
12'h789,
12'h9AB,
12'hE65,
12'hF87,
12'hD63,
12'hEA3,
12'hFEA
};

But using data_out from the BRAM as an index didn’t work. I switches to a switch case which did work but is less easily interchangable for a new palette :

assign r_out = (rec==0) ? rpi_color : colors[3:0];
assign g_out = (rec==0) ? rpi_color : colors[7:4];
assign b_out = (rec==0) ? rpi_color : colors[11:8];

always@(*) begin
case(data_out)
4'b0000: colors = 12'h222;
4'b0001: colors = 12'h322;
4'b0010: colors = 12'h432;
4'b0011: colors = 12'h743;
4'b0100: colors = 12'hB75;
4'b0101: colors = 12'hFA7;
4'b0110: colors = 12'hFC8;
4'b0111: colors = 12'h675;
4'b1000: colors = 12'h995;
4'b1001: colors = 12'h789;
4'b1010: colors = 12'h9AB;
4'b1011: colors = 12'hE65;
4'b1100: colors = 12'hF87;
4'b1101: colors = 12'hD63;
4'b1110: colors = 12'hEA3;
4'b1111: colors = 12'hFEA;
default: colors = 12'h222;
endcase
end 

Nice sunset color palette though :

I can only get the memory working with the same values for the 15×2 array. But I now have three palettes and can change between them with key presses !

***

Got a tour of the engraving workshop at the BA and saw them making multiple color woodblock prints similar to this :

 

The workshop head kept referring to the medium that stores the information. Could I do something similar with my four SRAM blocks ?

***

A kind of numbered grid on the edges of the screen :

I can already put text from a font on screen, this would just involve adding numbers putting other images behind the text.

A mirror effect :

Recentering the camera on the edge of the screen :

rotating images :

bit blitting parts of images to create superimposed windows:

Different resolutions on the same screen :

****

 

Some VGA hardware diagrams showing different sub systems like a sequencer, the sync gen, and multiple RAM planes. Would be cool to recreate this explicitly in FPGA and then prod each interface and show the results.

I have got BRAM working with simple VGA sync generated by the FPGA and rpi/OV camera input. Changing the clocking frequency for the address incrementing changes the shape of the pixels.

But so far from the camera I’m only getting noise. I believe this is because I’m naively trying to send a single bit at a time and the OV camera appears to send RGB colors in a two byte package. I’ll need to test on the new board.

I had many verilog struggles, the worst of which was causing the BRAM not to instantiate and various pins to be ignored.

ram ram0(
.din(data_in),
.write_en (rec),
.waddr (waddress),
.wclk (counter[0]),
.raddr (raddress),
.rclk (clk_div[1]),
.dout(data_out)
);

If you select clock divisions for rclk and wclk that are too slow, all kinds of seemingly buggy behaviour takes place. I only got to this answer by rebuilding the vga sync and BRAM from scratch several times.

I learned that you can in fact divide by whole numbers instead of bit shifting to divide :

raddress <= (h_counter/3)+((v_counter/3)*213);

The logic analyzer was essential for being able to see the signals of the rpi and the OV camera. The oscilloscope also helped reveal that the level shifter (along with longish breadboard wires) on the board was interfering with the OV data and clock signals. 

The documentation for the OV7670 camera and the registers is a nightmare for me to understand. I think I will not use this for a kit for NO SCHOOL and instead get rpi zero W1.1 and skip the board all together and just have a collection of useful components.

****

From the musée de la chasse et de la nature, this beautiful chest of drawers each with a seperate scene :

***

Just learned from this post https://baltazarstudios.com/calculator-cpu/ that “20% of the instructions in a typical program accounted for about 80% of the execution” and that this is how we got RISC instruction sets.

*****

Trying to get character display working. Found great site with 8×8 fonts here https://int10h.org/oldschool-pc-fonts/fontlist/font?ibm_bios . Couldn’t figure out how to get nice clear binary display from these font files though so eventually landed on this : https://gist.github.com/rothwerx/700f275d078b3483509f and formatted them with Notepad++:

reg [0:63] a =
{
8'b00000000,
8'b00000000,
8'b01111000,
8'b00001100,
8'b01111100,
8'b11001100,
8'b01110110,
8'b00000000
};

reg [0:63] b =
{
8'b11100000,
8'b01100000,
8'b01100000,
8'b01111100,
8'b01100110,
8'b01100110,
8'b11011100,
8'b00000000
};

address <= (h_counter/80)+((v_counter/60)*8);

address <= (h_counter[5:3])+((v_counter[5:3])*8);

reg char;
reg[14:0] address;
wire [5:0] short_address;

assign short_address = address[5:0];

wire [2:0]concat;
assign concat = {v_counter[4:3],h_counter[3]};

always @(*)
begin
case (cell_counter)
3'b000 : char = a[short_address];
3'b001 : char = b[short_address];
3'b010 : char = c[short_address];
3'b011 : char = d[short_address];
3'b100 : char = e[short_address];
3'b101 : char = f[short_address];
3'b110 : char = g[short_address];
3'b111 : char = h[short_address];
default : char = i[short_address];
endcase
end

reg [2:0]cell_counter;

//10 by 7.5 copies of the letter 'a'
always @(posedge clk_div[1]) begin

address <= (h_counter[5:3])+((v_counter[5:3])*8);

cell_counter <= (h_counter/64) + ((v_counter/64)*10);
end

***

Different parameters of a raster (made using this site https://www.uwarp.design/pixel-text):

  • pixel gap

  • pixel shape

  • pixel size

***

Nice article https://www.quantamagazine.org/how-the-bird-eye-was-pushed-to-an-evolutionary-extreme-20260513/ talking about how bird’s eyes work. Mentioning that nature acts more like a tinkerer than an inventor :

“Evolution is not really like an inventor; it acts more like a tinkerer,” he said, citing a 1977 essay, “Evolution and Tinkering (opens a new tab),” by the French biologist François Jacob. “It takes parts that have existed long before, and it recombines, reinvents, and reshapes.”

***

This article talking about blending different frames in N64 and playstation https://phoboslab.org/log/2026/05/n64-additive-blending . They describe four different ways to mix frames.
0: (src + dst) / 2 
1: src + dst 
2: dst - src 
3: dst + src/4

****

I soldered the 0V7670 board using the end of supplies I had :

and with the camera plugged in :

Sits nicely atop the Arduino but I placed the keys a smidgen too close together for keycaps to sit on them without rubbing together. So far it doesn’t program, have tried two flash ICs and will continue trouble shooting tomorrow.

I replaced the flash IC twice, resoldered the oscillator, and went around the header and the FPGA to remelt pins. Still no luck. I may have to replace the FPGA next even though the soldering process was extra smooth and it was brand new. I’ve double checked PLL power, the 2.5V diode pin, the 1.2V and 3.3 power points on the board and they look normal. The 1.2V LDO is working. Hmmmm