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を使わない場合は表示用のコードはコメントアウトしてください.

main.cpp

Committer:
smorioka
Date:
2014-09-24
Revision:
0:f31ceb6058cb

File content as of revision 0:f31ceb6058cb:

/*
 * 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