OV7670 Camera with STM32F0 Discovery board

Cameras! This is a project I’ve wanted to do for ages – I’ve had the module since early 2015. Using an STM32F0 (my shiny new one!), an ILI9163 160×128 LCD and an OV7670 FIFO camera module, I was able to achieve 20fps video on this tiny LCD with only a few tips and tricks!

Video Modes
I’ve got 3 different video modes in mind, two of which are implemented now.

Windowed viewing
Windowed viewing literally just consists of blitting a window of pixels from the video stream to the LCD, essentially taking a 160×128 pixel window out of the video stream (currently running at 320×240). This is probably the least processor intensive and just consists of 7 logical evaluations and a jump:
(x>=windowLeft && x<windowLeft+160 && y>=windowTop && y<windowTop+128)

For even faster performance, the window can be assigned to the one of the top corners, removing 4 of the logical evaluations, for example in the top left corner:
(x<160 && y<128). Another advantage of this method is that if you’re displaying the top left or right corner of the image, you can completely stop grabbing FIFO data once you’ve grabbed 128 lines, essentially allowing you to scrap the bottom half with minimal overheads.

Extracting the top corner of an image to display on the LCD

Software pixel skip scaling
Many downscaling methods exist, notably bilinear and bicubic methods. These are both reasonably time consuming on a microcontroller for live scaling so I won’t be looking into these. There is also simple averaging of pixels which is less processor intensive but also requires splitting an RGB565 packed pixel into the individual RGB components, summing and dividing (though can be implemented with a shift or multiply and shift for fractional). In my implementation, I’ve merely used pixel and line skipping. With the incoming image from the camera at a resolution of 320×240 and my LCD having a resolution of 160×128, implementing pixel and line skipping is reasonably simple.

To start with, every other X pixel is skipped meaning you only write half of the X pixels to the LCD (320/2 = 160, tada!). For the line skipping however, this isn’t quite as easy as a fractional skip is required (240/128 = every 1.875th line). I tried implementing this with integer math but it was a little dodgy and didn’t always print the bottom row of pixels so instead, I used (gasp!) standard floating point math. By doing some simple mathematics and a comparison, a fractional line skip can be written:

y = (int)0 to 240
yskipold = (int) -1
yskip = (float)y*128.0f/240.0f

if((int)yskip != yskipold) {
yskipold = (int)yskip
else ScrapLine()

As can be seen above, yskip will contain a fractional number which will count from 0 to 128 in steps of 128/240 (0.53333…). Comparing this number to an integer version of itself and looking for a change, allows for fractional line skipping and scaling to the screen size!

scalep2.pngPixel skip example

Memory video buffer
Currently, my STM32F0 doesn’t have enough memory to store anywhere near enough of the video buffer (320×240 @ 16bpp = 153.6kB RAM, 160×128 @ 16bpp = 40.96kB RAM). I have started designing a dedicated board that will feature this camera, an ILI9163 LCD and an STM32F3 microcontroller (first project featuring such a beast!) which will be sent off with my next batch of PCBs where I’m hoping the chosen microcontroller will have enough RAM for me to at least store a 160×128 buffer. My main aim here is so I can have a go at writing some fast image processing algorithms, predominantly edge detection and software blur/sharpening. One issue with general image processing algorithms however is the fact that they require fast access to the individual colour channels R, G and B. This is an issue as unpacking an RGB565 byte and repacking is actually quite a processor intensive task. A thought I have had however on space is storing the pixels as RGB332 (8 bit colour), reducing the RAM requirement. Fortunately, RGB332 to RGB565 conversion can be done really quickly through the use of LUTs. This also allows me to define colour palettes which may help eradicate that vile 256 colour look!

Camera PCB  Rev.1 featuring onboard IR LEDs and an ambient light sensor

Surprisingly, I was able to get around 7fps of performance with no optimization, running at 48MHz. For a first run, this doesn’t seem particularly bad to me though for those of you who are gamers – that is vile! Compiling under O3 increases the frame rate to 15fps which is about the frame cap without messing with the OV7670 clock settings… and overclocking the STM32F051 and ILI9163.

While 48MHz is great and all, sometimes that extra little nugget of speed is required! The STM32F051 can be overclocked (though obviously shouldn’t be) up to 64MHz using the internal oscillator with settings of HSI/2 * PLL16 = 8/2*16 = 4*16 = 64MHz. Obviously this is running the STM32F051 outside of its normal operating conditions though this didn’t seem to be an issue for this application and resulted in no more glitches than normal. The ILI9163 however has a maximum SPI clock speed of 15.2MHz which with an STM32F051 running at 64MHz gives an SPI clock frequency of 64/2 = 32MHz, over double the specified SPI frequency.While this isn’t a particularly wise way to run the LCD and would never do in a proper engineering situation, as a bit of fun, its nice to see that it’s achievable.

Me! The colours are pretty wish-wash but that’s not a big deal considering how cheap the setup is.

Frame rate cap
Without drastic increases in clock speed etc, the frame rate was generally capped to 15fps. The reason for this being even if the OV7670 was running at 30fps, the rate at which data could be acquired from the FIFO without being over written was longer than a single frame. This meant that the next frame could only be grabbed upon the following vsync pulse.

OV7670 spitting out frames at 30fps. The yellow trace is the read clock, aliased and messy

P1020327.JPG30fps without overclocking the STM32F0


High five camera?

There are two modes of running, waiting for a full frame (which generally leads to dropping another frame) though being well synchronized, and clearing the write pointer then just trying to read asap – hoping the reading is done slower than pixels are placed in the FIFO to avoid overrun! Both need work but here is a basic start.

The code is sloppy but can be found on my Github!

3 thoughts on “OV7670 Camera with STM32F0 Discovery board

  1. Hello. Have you maybe tested how the downscaling by skipping pixels and lines behaves for lower destination resolution? I’ll be downscaling this way a PAL/NTSC ADV7280 digital output to size of LED matrix, 32×16, don’t know what to expect

  2. Hello. Have you maybe tested how the downscaling by skipping pixels and lines behaves for lower destination resolution? I’ll be downscaling this way a 720×576 digital output from ADV7280 to size of a LED matrix (32×16). I don’t know what to expect

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s