UCSB ECE153B Final Project
Digital Camera - "Potato Cam"


Maxwell Jung and Baron Young



See pull requests for progress updates.


Overview

Create a digital camera capable of capturing and saving a photograph. A pre-made camera module will capture the image, and the TFT LCD will preview the captured image. The user can press the blue button on the Nucleo to save the previewed image to the microSD card.

Please checkout written reportfor details.

Challenges and Reflection

Porting Arduino drivers to Nucleo:

Arduino uses C++ to conveniently abstract hardware into objects. This object oriented approach allows similar hardware to abstractly share code using inheritance and virtual functions. For example, any device that can print text inherits the printer class which lets those objects call write()/print() method. Every arduino driver we used called the write/print method at some point to send some data to a peripheral (ie. Serial.print(), lcd.print(), spi.write(), etc). Properly configuring the inheritances and reimplementing nearly all hardware specific functions to match Arduino behavior was a major challenge. Some of the functions in Arduino couldn’t be exactly translated to Nucleo due to physical hardware differences, in which case we had to mix and match different hardware functions according to the STM reference manual.

Arducam SPI issues:

Arducam works by configuring the camera sensor (OV2640) using I2C and reading/writing image data using SPI. Apparently, the default SPI timings on STM boards are not recognized by Arducam’s proprietary SPI/I2C IC chip used to communicate with the OV2640 sensor. (see issue #2 and issue #127) The abnormality is suspected to be caused by the timing delay between data frames. The suspected solution (though we didn’t have the time to implement and test it) is to use DMA to saturate the internal SPI FIFOs. This ensures that the SPI protocol packs adjacent data frames as tight as possible, minimizing the delay between each data frame. Our workaround was to connect Arducam to Arduino and treat the Arducam + Arduino as a new module. Abstractly speaking, the Arducam+Arduino module acted as a black box with UART transmit and receive pins. The transmit pin is used to command the module to take a photo, and the receive pin is used to retrieve each byte of the image from the start to end. Because we’re using UART limited to 9000 buad rate to retrieve image data, it takes around 9 seconds to get a new image into the Nucleo. Increasing the baud rate beyond 9000 transfers the image faster, but also greatly increases the chance of data loss/image corruption. Through trial and error, 9000 baud rate was determined to be the ideal baud rate to minimize image corruption.

Stack overflow:

We encountered a weird bug while testing the SDcard library. For some reason, calling sd.open() after enter the lcd.draw() function crashed the program, but calling the function before would work fine. Turns out, the lcd.draw() method was allocating just enough local variables in the call stack that calling sd.open() immediately after entering the function would overflow the stack. The solution was to either move one of the large local variables into static memory, or to increase the stack size. We chose the later option and increased the stack size from the default 0x400 to 0x800 in the startup assembly file.

Keil free edition max compilable image size:

The free edition of Keil used for the class limits the maximum size of the compiled program to 32KB. We encountered this issue early in the project due to the large amount of source code imported from Arduino drivers. (We also suspect that using C++ instead of C contributed to larger program size) The remedy was to turn on compiler optimization flag -Oz, which reduced the image size by a factor of ~5. We later reencountered the size limitation when testing the jpeg decoder library, but luckily, we were able to skip the test program and integrate the library straight into the project.

JPEG vs bitmap:

Bitmap is the most intuitive representation of an image since each pixel of the image corresponds to 2 bytes. However, bitmap's convenience makes their filesize large, too large to fit into Nucleo’s 128KB SRAM (a 320x240 bitmap image is at least 150KB). JPEG is much much smaller with an average size of 7KB for the same 320x240 image. This allows the JPEG image to be stored as a contiguous array in memory. The data from the array is used to write the image file to disk. The LCD, however, can only display bitmap images. As a result, the JPEG array must be decoded into bitmap before getting displayed on the LCD. Because the entire bitmap array cannot fit into memory, the decoder library decodes the entire JPEG array by using smaller blocks.

Contact Info

[email protected]

[email protected]