Sample code of section 3 in Nov 2014 issue of the Interface Magazine, published by CQ publishing in Japan. CQ出版社インターフェース誌 2014年11月号3章に掲載のサンプルコードです. FRDM-K64FにOV7670カメラを接続して映像を取得するとともに,簡単なフィルタ処理も施すサンプルです.このコードのうちカメラ制御部には,Sadaei Osakabe氏のコードを流用させていただいています.

Dependencies:   SDFileSystem TextLCD mbed

このコードでは,Arduino用のLCDシールド(http://www.switch-science.com/catalog/724/)を接続することを想定しています.ただしLCDは必須ではないので,LCDを使わない場合は表示用のコードはコメントアウトしてください.

Revision:
0:f31ceb6058cb
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/main.cpp	Wed Sep 24 20:44:56 2014 +0000
@@ -0,0 +1,304 @@
+/*
+ * Copyright (c) 2014, Sumio Morioka
+ * All rights reserved.
+ *
+ * This source code was originally written by Dr.Sumio Morioka for use in the Nov 2014 issue of 
+ * "the Interface magazine", published by CQ publishing Co.Ltd in Japan (http://www.cqpub.co.jp).
+ * The author has no responsibility on any results caused by using this code.
+ *
+ * - Distribution date of this code: Sep 24, 2014
+ * - Author: Dr.Sumio Morioka (http://www002.upp.so-net.ne.jp/morioka)
+ *
+ *
+ * IMPORTANT NOTICE:
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *   * Redistributions of source code must retain the above copyright
+ *     notice, this list of conditions and the following disclaimer.
+ *   * Redistributions in binary form must reproduce the above copyright
+ *     notice, this list of conditions and the following disclaimer in the
+ *     documentation and/or other materials provided with the distribution.
+ *   * Neither the name of the copyright holder nor the
+ *     names of its contributors may be used to endorse or promote products
+ *     derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+ 
+#include "mbed.h"
+#include "SDFileSystem.h"
+#include "TextLCD.h"
+#include "ov7670.h"
+
+DigitalOut led1(LED1), led2(LED2), led3(LED3);
+SDFileSystem sd(PTE3, PTE1, PTE2, PTE4, "sd");      // MOSI, MISO, SCK, CS
+TextLCD lcd(PTD3, PTD2, PTA2, PTB23, PTA1, PTB9);   // Arduino LCD sheild; 
+
+OV7670 camera(
+    PTE25,PTE24,            // SDA,SCL(I2C / SCCB)
+    PTB2,PTB3,PTB10,        // VSYNC,HREF,WEN(FIFO)
+    PTC5,PTC7,PTC0,PTC9,PTC8,PTC1,PTB19,PTB18,    // D7-D0
+    PTB11,PTC11,PTC10);     // RRST,OE,RCLK    
+
+Timer tmr;
+
+//#define QQVGA
+#define QVGA
+//#define VGA34
+
+#ifdef QQVGA
+#define SIZEX 160
+#define SIZEY 120
+#endif
+
+#ifdef QVGA
+#define SIZEX 320
+#define SIZEY 240
+#endif
+
+#ifdef VGA34
+#define SIZEX 480
+#define SIZEY 360
+#endif
+
+unsigned char image_buf_g[SIZEX * SIZEY * 3];
+
+void cam_cap(void);
+void save_bmp(void);
+
+int memfree(void)
+{
+    int ret = 1;
+    while (1) {
+        char    *p  = (char *)malloc(ret);
+        if (p == NULL)
+            break;
+        free(p);
+        ret++;
+    }
+    return (ret - 1);
+}
+
+int main() 
+{
+    led1 = 0;
+    led2 = 0;
+    led3 = 0;
+
+    /////////////////////////////////////////
+    // init camera
+    camera.WriteReg(0x12, 0x80);            // com7; reset
+    wait_ms(200);
+
+    camera.InitDefaultReg();
+
+    // negate vsync
+    camera.WriteReg(0x15, 0x02);            // com10; negative vsync
+
+#ifdef QQVGA
+    camera.InitQQVGA();
+#endif
+#ifdef QVGA
+    camera.InitQVGA();
+#endif
+#ifdef VGA34
+    camera.InitVGA_3_4();
+#endif
+
+    // data format
+    camera.WriteReg(0x12, 0x04 + 0);    // com7 RGB (bit1...test pattern)
+    camera.WriteReg(0x40, 0xD0);    // com15 RGB565
+    camera.WriteReg(0x8c, 0x00);    // RGB444
+
+    wait_ms(300);
+
+    ///////////////////////////////////////
+    // display free memory size
+    int cur_mem = memfree();
+    lcd.locate(0, 0);
+    lcd.printf("mem %d", cur_mem);
+
+    ///////////////////////////////////////
+    // capture
+    tmr.reset();
+    tmr.start();    // timer
+
+    cam_cap();      // capture photo
+
+    tmr.stop();     // timer
+    lcd.locate(0, 0);
+    lcd.printf("cap %dms", tmr.read_ms());
+
+    ////////////////////////////////////////
+    // apply filter
+    tmr.reset();
+    tmr.start();    // timer
+    {
+        int kernel_size = 3;
+        int kernel_dotnum   = kernel_size * kernel_size;
+        int kernel_halfsize = (kernel_size - 1) / 2;
+
+        // calc average value
+        for (int y = 0; y < SIZEY; y++) {
+            int     r_new, g_new, b_new;
+
+            // scan
+            for (int x = 0; x < SIZEX; x++) {
+                int     r, g, b;
+
+                int acc_r   = 0;
+                int acc_g   = 0;
+                int acc_b   = 0;
+
+                for (int ky = 0; ky < kernel_size; ky++) {
+                    int ypos    = y - kernel_halfsize + ky;
+
+                    if (ypos < 0 || ypos >= SIZEY)
+                        continue;
+
+                    for (int kx = 0; kx < kernel_size; kx++) {
+                        int xpos    = x - kernel_halfsize + kx;
+
+                        if (xpos < 0 || xpos >= SIZEX)
+                            continue;
+
+                        b   = image_buf_g[(ypos * SIZEX + xpos) * 3];
+                        g   = image_buf_g[(ypos * SIZEX + xpos) * 3 + 1];
+                        r   = image_buf_g[(ypos * SIZEX + xpos) * 3 + 2];
+
+                        acc_r   += r;
+                        acc_g   += g;
+                        acc_b   += b;
+                    }
+                }
+
+                r_new   = (unsigned char)(acc_r / kernel_dotnum);
+                g_new   = (unsigned char)(acc_g / kernel_dotnum);
+                b_new   = (unsigned char)(acc_b / kernel_dotnum);
+
+                image_buf_g[(y * SIZEX + x) * 3]     = b_new;
+                image_buf_g[(y * SIZEX + x) * 3 + 1] = g_new;
+                image_buf_g[(y * SIZEX + x) * 3 + 2] = r_new;
+            }
+        }
+    }
+    tmr.stop();     // timer
+    lcd.locate(0, 1);
+    lcd.printf("flt %dms", tmr.read_ms());
+
+    ////////////////////////////////////////
+    // save BMP
+    tmr.reset();
+    tmr.start();    // timer
+
+    save_bmp();
+
+    tmr.stop();     // timer
+//  lcd.locate(0, 1);
+//  lcd.printf("bmp %dms", tmr.read_ms());
+
+    //////////////////////////////////////////////////
+    // (stop)
+    while (1) {
+        led1 = 1;
+        wait_ms(100);
+   }
+}
+
+
+void save_bmp(void)
+{
+    FILE *fp_bmp;
+    unsigned char sort[3];
+    unsigned int buf_ptr = 0;
+
+    led3 = 1;
+
+    fp_bmp  = fopen("/sd/cam.bmp", "wb");
+
+    /////////////////////////
+    // file header
+    /////////////////////////
+    fprintf(fp_bmp, "BM");
+    int val = 14 + 40 + SIZEX * SIZEY * 3;   // file size
+    fprintf(fp_bmp, "%c%c%c%c", val % 0x100, val / 0x100, val / 0x10000, val / 0x1000000);
+    fprintf(fp_bmp, "%c%c%c%c%c%c%c%c", 0, 0, 0, 0, 0x36, 0, 0, 0);
+
+    /////////////////////////
+    // information header
+    /////////////////////////
+    fprintf(fp_bmp, "%c%c%c%c", 0x28, 0, 0, 0);  // header size
+    fprintf(fp_bmp, "%c%c%c%c", SIZEX % 0x100, SIZEX / 0x100, SIZEX / 0x10000, SIZEX / 0x1000000);
+    fprintf(fp_bmp, "%c%c%c%c", SIZEY % 0x100, SIZEY / 0x100, SIZEY / 0x10000, SIZEY / 0x1000000);
+    fprintf(fp_bmp, "%c%c", 1, 0);               // # of plane
+    fprintf(fp_bmp, "%c%c", 24, 0);              // bit count
+    fprintf(fp_bmp, "%c%c%c%c", 0, 0, 0, 0);     // compression
+    val = SIZEX * SIZEY * 3;         // data size
+    fprintf(fp_bmp, "%c%c%c%c", val % 0x100, val / 0x100, val / 0x10000, val / 0x1000000);
+    fprintf(fp_bmp, "%c%c%c%c", 0, 0, 0, 0);
+    fprintf(fp_bmp, "%c%c%c%c", 0, 0, 0, 0);
+    fprintf(fp_bmp, "%c%c%c%c", 0, 0, 0, 0);
+    fprintf(fp_bmp, "%c%c%c%c", 0, 0, 0, 0);
+
+    for (int y = 0;y < SIZEY;y++) {    
+        for (int x = 0;x < SIZEX;x++) {
+            sort[0] = image_buf_g[buf_ptr++];
+            sort[1] = image_buf_g[buf_ptr++];
+            sort[2] = image_buf_g[buf_ptr++];
+
+            fprintf(fp_bmp, "%c%c%c", sort[2], sort[1], sort[0]);       // B,G,R
+        }
+    }
+
+    fclose(fp_bmp);
+
+    led3 = 0;
+}
+
+//void cam_cap(Arguments* input, Reply* output)
+void cam_cap(void)
+{
+    unsigned int d1, d2;
+    unsigned char sort[3];
+    unsigned int buf_ptr = 0;
+
+    led2 = 1;
+
+    camera.CaptureNext();   // sample start!
+
+    while(camera.CaptureDone() == false)
+        ;
+
+    camera.ReadStart();     // reset pointer
+
+    for (int y = 0;y < SIZEY;y++) {    
+        for (int x = 0;x < SIZEX;x++) {
+            d1 = camera.ReadOneByte() ; // upper nibble is XXX , lower nibble is B
+            d2 = camera.ReadOneByte() ; // upper nibble is G   , lower nibble is R
+
+            // RGB565 to RGB888
+            sort[0] = ((d1 & 0xF8) >> 3) << 3;      // R
+            sort[1] = ( ((d1 & 0x07) << 3) + ((d2 & 0xE0) >> 5) ) << 2;     // G
+            sort[2] = (d2 & 0x1F) << 3;             // B
+
+            image_buf_g[buf_ptr++]  = sort[2];
+            image_buf_g[buf_ptr++]  = sort[1];
+            image_buf_g[buf_ptr++]  = sort[0];
+        }
+    }
+
+    camera.ReadStop();
+
+    led2 = 0;
+}
+
+// end of file
\ No newline at end of file