Arduinoで大気圧センサを使ってみる [Arduino]
大気圧センサを入手したので,Arduinoで動かしてみる.使用したのは,秋月電子の,「MPL115A2使用大気圧センサーモジュールキット(I2C) Ver2」.
ネットで調べると,Arduino用のライブラリが公開されていたので,ありがたく使わせてもらう.参照したのは,Arduinoで遊ぶページにある,「MPL115A2用ライブラリ」だ.
あとは,前回作った7セグLEDライブラリと組み合わせて,測定した大気圧を7セグLEDに表示してみる.
7セグLEDとの接続は前回と同じなので,大気圧センサとの接続部分のみ示そう.回路図はこんな感じ.
で,スケッチ.
測定値のばらつきが多いようなので,10回測定して平均値を採用してる.
あと,本来は高度に応じて補正を入れて,海面更正気圧を求めるべきだが,ひとまず測定値そのままにしている.
こちらの使用環境ではおおよそ5気圧ほど低くなる計算(高度からの換算値)で,補正後の値を気象庁の気圧値(たとえば東京はこんな感じ)と比較してもおおよそ一致しているので,測定は正しく行えているようである.
で,実際の動作状況はこんなかんじ.
大気圧測定ライブラリを利用すると,とても簡単に使える.作成者に感謝.
お試しあれ.
ネットで調べると,Arduino用のライブラリが公開されていたので,ありがたく使わせてもらう.参照したのは,Arduinoで遊ぶページにある,「MPL115A2用ライブラリ」だ.
あとは,前回作った7セグLEDライブラリと組み合わせて,測定した大気圧を7セグLEDに表示してみる.
7セグLEDとの接続は前回と同じなので,大気圧センサとの接続部分のみ示そう.回路図はこんな感じ.
で,スケッチ.
#include <Wire.h> #include <MPL115A2.h> #include <MsTimer2.h> #include <Led7segment.h> const int anode_pins[] = { 12, // A 8, // B 5, // C 3, // D 2, // E 11, // F 6, // G 4, // DP }; const int cathode_pins[] = { 13, // DIG.1 10, // DIG.2 9, // DIG.3 7, // DIG.4 }; const int ANODE_PINS_NUM = sizeof(anode_pins) / sizeof(anode_pins[0]); const int CATHODE_PINS_NUM = sizeof(cathode_pins) / sizeof(cathode_pins[0]); unsigned int display_buffer[CATHODE_PINS_NUM] = {0}; void write_1digit(const unsigned char bitmap) { for (int i = 0; i < ANODE_PINS_NUM; i++) { digitalWrite(anode_pins[i], bitmap & (1 << i) ? HIGH : LOW); } } void clear() { for (int i = 0; i < ANODE_PINS_NUM; i++) { digitalWrite(anode_pins[i], LOW); } } void write() { for (int i = 0; i < CATHODE_PINS_NUM; i++) { digitalWrite(cathode_pins[i], LOW); write_1digit(display_buffer[i]); delayMicroseconds(100); clear(); digitalWrite(cathode_pins[i], HIGH); } } void print_7seg(unsigned char *buff) { noInterrupts(); for (int i = 0; i < CATHODE_PINS_NUM; i++) { display_buffer[i] = buff[i]; } interrupts(); } void setup() { for (int i = 0; i < ANODE_PINS_NUM; i++) { pinMode(anode_pins[i], OUTPUT); } for (int i = 0; i < CATHODE_PINS_NUM; i++) { pinMode(cathode_pins[i], OUTPUT); digitalWrite(cathode_pins[i], HIGH); } MsTimer2::set(1, write); MsTimer2::start(); MPL115A2.begin(); } void loop() { unsigned char buff[CATHODE_PINS_NUM] = {0}; const unsigned int numeric_digits = CATHODE_PINS_NUM; const unsigned int frac_digits = 3; Led7segment led; double pressure = 0.0; for (int i = 0; i < 10; i++) { pressure += MPL115A2.read(); delay(100); } pressure /= 10.0; led.print(buff, pressure, numeric_digits, frac_digits); print_7seg(buff); }
測定値のばらつきが多いようなので,10回測定して平均値を採用してる.
あと,本来は高度に応じて補正を入れて,海面更正気圧を求めるべきだが,ひとまず測定値そのままにしている.
こちらの使用環境ではおおよそ5気圧ほど低くなる計算(高度からの換算値)で,補正後の値を気象庁の気圧値(たとえば東京はこんな感じ)と比較してもおおよそ一致しているので,測定は正しく行えているようである.
で,実際の動作状況はこんなかんじ.
大気圧測定ライブラリを利用すると,とても簡単に使える.作成者に感謝.
お試しあれ.
タグ:大気圧センサ
Arduinoで7セグLEDを使ってみる [Arduino]
秋月電子の,「ダイナミック接続4桁高輝度赤色7セグメントLED表示器 カソードコモン カソード共通接続」OSL40562-LRを入手したので,Arduinoで動かしてみる.
今回は,ArduinoのIOピンで直接制御してみる.高輝度で点灯させるにはトランジスタを併用してLEDへの電流を確保する必要があるが,ひとまずIOピンから出力できる電流量で点灯させてみる.
Arduino Unoでは,IOピンの定格電流は40mAのようなので,これを前提に考えると...
OSL40562-LRの順方向電圧はtyp2Vだが,Minが1.8Vなのでそれで計算する.
ArduinoのIOは5Vなので,
5V - 1.8 V = 3.2V
小数点も含めると8個のLEDがあるので,Totalで30mAぐらいに収めるとすると,1LEDあたり,
30mA / 8 = 3.75mA
3.2V / 3.75mA = 0.85KΩ
ということになる.
というわけで,手元にある抵抗から近いものを探して820Ωを採用.
実際に点灯させてみれば分かるが,室内で見ている限りこれで十分な視認性が得られる.
こんな感じで接続する.
あとは,Arduino側のスケッチだが,今回は,Adafruitが公開しているLibraryを参考に改変して,出力する桁数を指定可能な7セグLED点灯用ライブラリを作ってみた.
表示テスト用のスケッチとあわせて示そう.
まず,ライブラリだが,以下の3つのファイルからなる.
Led7segment.h
Led7segment.cpp
keywords.txt
で,適当なディレクトリに,Led7segmentディレクトリを作成し,上記3つのファイルを保存してやればよい.
順にコードを示そう.
まず,Led7segment.h
次に,Led7segment.cpp
最後に,keywords.txt
ダウンロードするなら,こちらから.
Led7segment.zip.jpg
ただし,So-netのブログでは,zip圧縮ファイルのアップロードができないため,zip圧縮してから拡張子に.jpgをつけて無理矢理アップロードしている.
なので.ダウンロードしたら.jpgを消してzipファイルとして展開してやってください.
また,ライブラリの追加方法は,「arduino ライブラリ 追加」等で検索してみてください.
で,表示テスト用のスケッチは,Led_7seg.ino.
Led7segmentライブラリの使い方は,Led7segment.hと,これを見れば分かると思う.
なお,このスケッチではMsTimer2ライブラリを使用しているので,別途追加してやってください.
この表示テスト用スケッチでは,
それでは,お試しあれ.
今回は,ArduinoのIOピンで直接制御してみる.高輝度で点灯させるにはトランジスタを併用してLEDへの電流を確保する必要があるが,ひとまずIOピンから出力できる電流量で点灯させてみる.
Arduino Unoでは,IOピンの定格電流は40mAのようなので,これを前提に考えると...
OSL40562-LRの順方向電圧はtyp2Vだが,Minが1.8Vなのでそれで計算する.
ArduinoのIOは5Vなので,
5V - 1.8 V = 3.2V
小数点も含めると8個のLEDがあるので,Totalで30mAぐらいに収めるとすると,1LEDあたり,
30mA / 8 = 3.75mA
3.2V / 3.75mA = 0.85KΩ
ということになる.
というわけで,手元にある抵抗から近いものを探して820Ωを採用.
実際に点灯させてみれば分かるが,室内で見ている限りこれで十分な視認性が得られる.
こんな感じで接続する.
あとは,Arduino側のスケッチだが,今回は,Adafruitが公開しているLibraryを参考に改変して,出力する桁数を指定可能な7セグLED点灯用ライブラリを作ってみた.
表示テスト用のスケッチとあわせて示そう.
まず,ライブラリだが,以下の3つのファイルからなる.
Led7segment.h
Led7segment.cpp
keywords.txt
で,適当なディレクトリに,Led7segmentディレクトリを作成し,上記3つのファイルを保存してやればよい.
順にコードを示そう.
まず,Led7segment.h
/* * 数値を7セグLED用向けビットマップ値に変換する */ #ifndef LED_7SEGMENT_H #define LED_7SEGMENT_H class Led7segment { public: void print(unsigned char *buff, double value, unsigned int numeric_digits, unsigned int frac_digits = 1); /* * buff : 格納先バッファ(呼び出し元で必要なサイズ分を用意すること) * value : 変換する値 * numeric_digits : 出力する桁数(-符号含む桁数."."は含まない).例えば出力が"12.34"なら4. * frac_digits : 出力する小数点以下桁数(桁数不足で表示できない場合は丸められる) */ private: void write(unsigned char *buff, unsigned int x, unsigned int num, bool is_dot = false); void write_minus(unsigned char *buff, int display_index); void write_space(unsigned char *buff, int display_index); void write_error(unsigned char *buff, int display_index); }; #endif // LED_7SEGMENT_H
次に,Led7segment.cpp
/* * This is a library for 7segment LED OSL40562-LR. * * function Led7segment::print were copied and modified from * Adafruit-LED-Backpack-Library(original function: Adafruit_7segment::printFloat). * https://github.com/adafruit/Adafruit-LED-Backpack-Library */ /*************************************************** This is a library for our I2C LED Backpacks Designed specifically to work with the Adafruit LED Matrix backpacks ----> http://www.adafruit.com/products/ ----> http://www.adafruit.com/products/ These displays use I2C to communicate, 2 pins are required to interface. There are multiple selectable I2C addresses. For backpacks with 2 Address Select pins: 0x70, 0x71, 0x72 or 0x73. For backpacks with 3 Address Select pins: 0x70 thru 0x77 Adafruit invests time and resources providing this open source code, please support Adafruit and open-source hardware by purchasing products from Adafruit! Written by Limor Fried/Ladyada for Adafruit Industries. MIT license, all text above must be included in any redistribution https://github.com/adafruit/Adafruit-LED-Backpack-Library/blob/master/license.txt ****************************************************/ #include <math.h> #include "Led7segment.h" namespace { const unsigned char digits_table[] = { /* _____ * | A | * F| |B * |_____| * | G | * E| |C * |_____| o DP * D */ /* DP G F E D C B A */ 0x3F, /* 0 : 0 0 1 1 1 1 1 1 */ 0x06, /* 1 : 0 0 0 0 0 1 1 0 */ 0x5B, /* 2 : 0 1 0 1 1 0 1 1 */ 0x4F, /* 3 : 0 1 0 0 1 1 1 1 */ 0x66, /* 4 : 0 1 1 0 0 1 1 0 */ 0x6D, /* 5 : 0 1 1 0 1 1 0 1 */ 0x7D, /* 6 : 0 1 1 1 1 1 0 1 */ 0x07, /* 7 : 0 0 1 0 0 1 1 1 */ 0x7F, /* 8 : 0 1 1 1 1 1 1 1 */ 0x6F, /* 9 : 0 1 1 0 1 1 1 1 */ 0x00, /* : 0 0 0 0 0 0 0 0 */ 0x40, /* - : 0 1 0 0 0 0 0 0 */ 0x80, /* . : 1 0 0 0 0 0 0 0 */ }; const int SPACE_INDEX = 10; const int MINUS_INDEX = 11; const int DOT_INDEX = 12; } // namespace void Led7segment::print(unsigned char *buff, double value, unsigned int numeric_digits, unsigned int frac_digits) { const unsigned int base = 10; bool is_minus = false; int display_index = numeric_digits - 1; if (value < 0) { is_minus = true; numeric_digits--; value *= -1; } if (numeric_digits <= frac_digits) { frac_digits = numeric_digits - 1; } unsigned long to_int_factor = pow(base, frac_digits) + 0.5; unsigned long display_value = value * to_int_factor + 0.5; unsigned long max_limit = pow(base, numeric_digits) + 0.5; while (display_value >= max_limit) { to_int_factor /= base; display_value = value * to_int_factor + 0.5; frac_digits--; } if (to_int_factor != 0) { if (display_value != 0) { for (int i = 0; (display_value != 0) || (i <= frac_digits); i++) { const bool is_dot = (frac_digits != 0) && (i == frac_digits); write(buff, display_index--, display_value % base, is_dot); display_value /= base; } } else { for (int i = 0; i <= frac_digits; i++) { const bool is_dot = (frac_digits != 0) && (i == frac_digits); write(buff, display_index--, 0, is_dot); } } if (is_minus == true) { write_minus(buff, display_index--); } while (display_index >= 0) { write_space(buff, display_index--); } } else { while (display_index >= 0) { write_error(buff, display_index--); } } } void Led7segment::write(unsigned char *buff, unsigned int i, unsigned int num, bool is_dot) { if (is_dot == true) { buff[i] = digits_table[num] | digits_table[DOT_INDEX]; } else { buff[i] = digits_table[num]; } } void Led7segment::write_minus(unsigned char *buff, int display_index) { write(buff, display_index, MINUS_INDEX); } void Led7segment::write_space(unsigned char *buff, int display_index) { write(buff, display_index, SPACE_INDEX); } void Led7segment::write_error(unsigned char *buff, int display_index) { write(buff, display_index, MINUS_INDEX); }
最後に,keywords.txt
Led7segment KEYWORD1 print KEYWORD2
ダウンロードするなら,こちらから.
Led7segment.zip.jpg
ただし,So-netのブログでは,zip圧縮ファイルのアップロードができないため,zip圧縮してから拡張子に.jpgをつけて無理矢理アップロードしている.
なので.ダウンロードしたら.jpgを消してzipファイルとして展開してやってください.
また,ライブラリの追加方法は,「arduino ライブラリ 追加」等で検索してみてください.
で,表示テスト用のスケッチは,Led_7seg.ino.
Led7segmentライブラリの使い方は,Led7segment.hと,これを見れば分かると思う.
なお,このスケッチではMsTimer2ライブラリを使用しているので,別途追加してやってください.
#include <MsTimer2.h> #include <Led7segment.h> const int anode_pins[] = { 12, // A 8, // B 5, // C 3, // D 2, // E 11, // F 6, // G 4, // DP }; const int cathode_pins[] = { 13, // DIG.1 10, // DIG.2 9, // DIG.3 7, // DIG.4 }; const int ANODE_PINS_NUM = sizeof(anode_pins) / sizeof(anode_pins[0]); const int CATHODE_PINS_NUM = sizeof(cathode_pins) / sizeof(cathode_pins[0]); unsigned int display_buffer[CATHODE_PINS_NUM] = {0}; void write_1digit(const unsigned char bitmap) { for (int i = 0; i < ANODE_PINS_NUM; i++) { digitalWrite(anode_pins[i], bitmap & (1 << i) ? HIGH : LOW); } } void clear() { for (int i = 0; i < ANODE_PINS_NUM; i++) { digitalWrite(anode_pins[i], LOW); } } void write() { for (int i = 0; i < CATHODE_PINS_NUM; i++) { digitalWrite(cathode_pins[i], LOW); write_1digit(display_buffer[i]); delayMicroseconds(100); clear(); digitalWrite(cathode_pins[i], HIGH); } } void print_7seg(unsigned char *buff) { noInterrupts(); for (int i = 0; i < CATHODE_PINS_NUM; i++) { display_buffer[i] = buff[i]; } interrupts(); } void setup() { for (int i = 0; i < ANODE_PINS_NUM; i++) { pinMode(anode_pins[i], OUTPUT); } for (int i = 0; i < CATHODE_PINS_NUM; i++) { pinMode(cathode_pins[i], OUTPUT); digitalWrite(cathode_pins[i], HIGH); } MsTimer2::set(1, write); MsTimer2::start(); } void loop() { unsigned char buff[CATHODE_PINS_NUM] = {0}; const unsigned int numeric_digits = CATHODE_PINS_NUM; const unsigned int frac_digits = 3; Led7segment led; for (long i = -1000; i <= 10000; i++) { double d = i; led.print(buff, d, numeric_digits, frac_digits); print_7seg(buff); delay(100); } for (long i = -1000; i <= 10000; i++) { double d = i / 10.0; led.print(buff, d, numeric_digits, frac_digits); print_7seg(buff); delay(100); } for (long i = -1000; i <= 10000; i++) { double d = i / 100.0; led.print(buff, d, numeric_digits, frac_digits); print_7seg(buff); delay(100); } for (long i = -1000; i <= 10000; i++) { double d = i / 1000.0; led.print(buff, d, numeric_digits, frac_digits); print_7seg(buff); delay(100); } }
この表示テスト用スケッチでは,
・-1000 〜 10000 まで,1 ずつ
・-100.0 〜 1000.0 まで,0.1 ずつ
・-10.00 〜 100.00 まで,0.01 ずつ
・-1.000 〜 10.000 まで,0.001ずつ
順に表示する動作になっていて,桁数が足りなくて表示できない値は「----」と表示されます.それでは,お試しあれ.
タグ:7セグLED
久しぶりにov7670+Arduino+Processingを動かしてみる [Arduino]
ov7670を動かしていたのはずいぶん前で,その後は放置していたのだが,久しぶりに動かしてみた.コード公開のご要望をいただいたので,最新の状態を公開してみる.
VGAとQQVGAに対応していて,QQVGAは当初はRGB444だったが,今はRGB565で動かしている.
ただ,あらかじめ断っておくが,シリアル通信で画像データを取得している部分がボトルネックになっているようで,画像の更新頻度(フレームレート)はかなり遅い.リアルタイムは全然無理で,パラパラマンガ以下である.
Arduinoのシリアル通信のライブラリに手を入れると改善できるのではないかと思うのだが...
ボーレートから計算した通信スピードに対して実際が遅すぎで,どこかに大きなオーバーヘッドがのってるような気がする.
では,まずはArduino側のコード.ov7670.inoとov7670reg.hだ.
最初にov7670.ino.
次に,ov7670reg.h
次にProcessing側のコード.こちらは,CameraControl.pde,Button.pde,CheckBox.pde,Command.pde,TextBox.pdeから構成されているので,順に示そう.あと,Font(.vlwファイル)は別途生成してください.
では,CameraControl.pde.
Button.pde.
CheckBox.pde.
Command.pde.
TextBox.pde.
Processingで作ったアプリの使い方は...
Arduinoがつながっている状態で起動すると,VGAで画像の連続取得が自動的に動きます.
あとは以下の画像内の説明を見ながら使ってみてください.
では,お試しあれ.
VGAとQQVGAに対応していて,QQVGAは当初はRGB444だったが,今はRGB565で動かしている.
ただ,あらかじめ断っておくが,シリアル通信で画像データを取得している部分がボトルネックになっているようで,画像の更新頻度(フレームレート)はかなり遅い.リアルタイムは全然無理で,パラパラマンガ以下である.
Arduinoのシリアル通信のライブラリに手を入れると改善できるのではないかと思うのだが...
ボーレートから計算した通信スピードに対して実際が遅すぎで,どこかに大きなオーバーヘッドがのってるような気がする.
では,まずはArduino側のコード.ov7670.inoとov7670reg.hだ.
最初にov7670.ino.
#include <avr/io.h> #include <Wire.h> #include "ov7670reg.h" // Pin const int WEN = 14; // PC0 const int RRST = 15; // PC1 const int OE = 16; // PC2 const int RCLK = 17; // PC3 const int VSYNC = 2; // PD2 INT0 const int D0 = 8; // PB0 const int D1 = 9; // PB1 const int D2 = 10; // PB2 const int D3 = 11; // PB3 const int D4 = 4; // PD4 const int D5 = 5; // PD5 const int D6 = 6; // PD6 const int D7 = 7; // PD7 volatile boolean isWriteEnable = false; const int BAYER_VGA = 0; const int RGB565_QQVGA = 1; int format = BAYER_VGA; void setPinMode() { pinMode(WEN, OUTPUT); pinMode(RRST, OUTPUT); pinMode(OE, OUTPUT); pinMode(RCLK, OUTPUT); pinMode(D0, INPUT ); pinMode(D1, INPUT ); pinMode(D2, INPUT ); pinMode(D3, INPUT ); pinMode(D4, INPUT ); pinMode(D5, INPUT ); pinMode(D6, INPUT ); pinMode(D7, INPUT ); attachInterrupt(0, setWriteEnable, FALLING); digitalWrite(WEN, LOW); digitalWrite(RRST, HIGH); digitalWrite(OE, HIGH); digitalWrite(RCLK, HIGH); } void setWriteEnable() { if (isWriteEnable) { digitalWrite(WEN, HIGH); isWriteEnable = false; } else { digitalWrite(WEN, LOW); } } void initFifo() { /* * __ __ __ __ __ __ * RCLK |__| |__ __| |__| * __ __ __ __ __ __ * RRST |__ __ __ __| * __ __ __ __ __ __ __ * OE |__ __ __ */ // RCLK RRST OE PORTC |= _BV(3) | _BV(1) | _BV(2); // H H H PORTC &= ~_BV(1); // H L H PORTC &= ~_BV(3); // L L H PORTC |= _BV(3); // H L H PORTC &= ~_BV(3); // L L H PORTC |= _BV(1); // L H H PORTC |= _BV(3); // H H H PORTC &= ~_BV(2); // H H L PORTC &= ~_BV(3); // L H L PORTC |= _BV(3); // H H L } const int QQVGA_HSIZE = 160; const int QQVGA_VSIZE = 120; const int QQVGA_DATABYTE = 2; const int VGA_HSIZE = 640; const int VGA_VSIZE = 480; const int VGA_DATABYTE = 1; int hsize = VGA_HSIZE; int vsize = VGA_VSIZE; int databyte = VGA_DATABYTE; void setDataFormat() { switch (format) { case BAYER_VGA: hsize = VGA_HSIZE; vsize = VGA_VSIZE; databyte = VGA_DATABYTE; break; case RGB565_QQVGA: hsize = QQVGA_HSIZE; vsize = QQVGA_VSIZE; databyte = QQVGA_DATABYTE; break; default: ; break; } } void readFifo() { const char HEX_TABLE[] = "0123456789ABCDEF"; byte dh, dl; for (int v = 0; v < vsize; v++) { for (int h = 0; h < hsize; h++) { for (int d = 0; d < databyte; d++) { PORTC |= _BV(3); // RCLK HIGH delayMicroseconds(1); dh = PIND >> 4; dl = PINB & 0x0F; PORTC &= ~_BV(3); // RCLK LOW Serial.print(HEX_TABLE[dh]); Serial.print(HEX_TABLE[dl]); } } } // read end PORTC &= ~_BV(3); // RCLK LOW PORTC |= _BV(2); // OE HIGH PORTC |= _BV(3); // RCLK HIGH } void writeRegister(int address, int data) { const int I2C_WRITE_ADDR = 0x42; Wire.beginTransmission(I2C_WRITE_ADDR >> 1); Wire.write(address); Wire.write(data); Wire.endTransmission(); } int readRegister(int address) { const int I2C_READ_ADDR = 0x43; Wire.beginTransmission(I2C_READ_ADDR >> 1); Wire.write(address); Wire.endTransmission(); Wire.requestFrom(I2C_READ_ADDR >> 1, 1); return Wire.read(); } void resetCamera() { writeRegister(REG_COM7, COM7_RESET); delay(200); } void initRegister() { initDefaultRegister(); initVsyncPolarity(); switch (format) { case BAYER_VGA: initBayerRGB(); initVGA(); break; case RGB565_QQVGA: initRGB565(); initQQVGA(); break; default: ; break; } } void initBayerRGB() { /* * GBGBGBGBGB * RGRGRGRGRG * GBGBGBGBGB * RGRGRGRGRG */ int reg_com7 = readRegister(REG_COM7); writeRegister(REG_COM7, reg_com7 | COM7_PBAYER); writeRegister(REG_RGB444, RGB444_DISABLE); writeRegister(REG_COM15, COM15_R00FF); writeRegister(REG_COM13, 0x08); /* No gamma, magic rsvd bit */ writeRegister(REG_COM16, 0x3d); /* Edge enhancement, denoise */ writeRegister(REG_REG76, 0xe1); /* Pix correction, magic rsvd */ writeRegister(REG_TSLB, 0x04); } void initRGB444() { int reg_com7 = readRegister(REG_COM7); writeRegister(REG_COM7, reg_com7 | COM7_RGB); writeRegister(REG_RGB444, RGB444_ENABLE | RGB444_XBGR); writeRegister(REG_COM15, COM15_R01FE | COM15_RGB444); writeRegister(REG_COM1, 0x40); /* Magic reserved bit */ writeRegister(REG_COM9, 0x38); /* 16x gain ceiling; 0x8 is reserved bit */ writeRegister(0x4f, 0xb3); /* "matrix coefficient 1" */ writeRegister(0x50, 0xb3); /* "matrix coefficient 2" */ writeRegister(0x51, 0x00); /* vb */ writeRegister(0x52, 0x3d); /* "matrix coefficient 4" */ writeRegister(0x53, 0xa7); /* "matrix coefficient 5" */ writeRegister(0x54, 0xe4); /* "matrix coefficient 6" */ writeRegister(REG_COM13, COM13_GAMMA | COM13_UVSAT | 0x2); /* Magic rsvd bit */ writeRegister(REG_TSLB, 0x04); } void initRGB565() { int reg_com7 = readRegister(REG_COM7); writeRegister(REG_COM7, reg_com7 | COM7_RGB); writeRegister(REG_RGB444, RGB444_DISABLE); writeRegister(REG_COM15, COM15_R00FF | COM15_RGB565); writeRegister(REG_COM1, 0x00); writeRegister(REG_COM9, 0x38); /* 16x gain ceiling; 0x8 is reserved bit */ writeRegister(0x4f, 0xb3); /* "matrix coefficient 1" */ writeRegister(0x50, 0xb3); /* "matrix coefficient 2" */ writeRegister(0x51, 0x00); /* vb */ writeRegister(0x52, 0x3d); /* "matrix coefficient 4" */ writeRegister(0x53, 0xa7); /* "matrix coefficient 5" */ writeRegister(0x54, 0xe4); /* "matrix coefficient 6" */ writeRegister(REG_COM13, COM13_GAMMA | COM13_UVSAT); writeRegister(REG_TSLB, 0x04); } void initVGA() { writeRegister(REG_HSTART, HSTART_VGA); writeRegister(REG_HSTOP, HSTOP_VGA); writeRegister(REG_HREF, HREF_VGA); writeRegister(REG_VSTART, VSTART_VGA); writeRegister(REG_VSTOP, VSTOP_VGA); writeRegister(REG_VREF, VREF_VGA); writeRegister(REG_COM3, COM3_VGA); writeRegister(REG_COM14, COM14_VGA); writeRegister(REG_SCALING_XSC, SCALING_XSC_VGA); writeRegister(REG_SCALING_YSC, SCALING_YSC_VGA); writeRegister(REG_SCALING_DCWCTR, SCALING_DCWCTR_VGA); writeRegister(REG_SCALING_PCLK_DIV, SCALING_PCLK_DIV_VGA); writeRegister(REG_SCALING_PCLK_DELAY, SCALING_PCLK_DELAY_VGA); } void initQQVGA() { writeRegister(REG_HSTART, HSTART_QQVGA); writeRegister(REG_HSTOP, HSTOP_QQVGA); writeRegister(REG_HREF, HREF_QQVGA); writeRegister(REG_VSTART, VSTART_QQVGA); writeRegister(REG_VSTOP, VSTOP_QQVGA); writeRegister(REG_VREF, VREF_QQVGA); writeRegister(REG_COM3, COM3_QQVGA); writeRegister(REG_COM14, COM14_QQVGA); writeRegister(REG_SCALING_XSC, SCALING_XSC_QQVGA); writeRegister(REG_SCALING_YSC, SCALING_YSC_QQVGA); writeRegister(REG_SCALING_DCWCTR, SCALING_DCWCTR_QQVGA); writeRegister(REG_SCALING_PCLK_DIV, SCALING_PCLK_DIV_QQVGA); writeRegister(REG_SCALING_PCLK_DELAY, SCALING_PCLK_DELAY_QQVGA); } void initVsyncPolarity() { writeRegister(REG_COM10, COM10_VS_NEG); } void initDefaultRegister() { writeRegister(REG_CLKRC, 0x01); writeRegister(REG_COM7, 0x00); /* VGA */ /* * Set the hardware window. These values from OV don't entirely * make sense - hstop is less than hstart. But they work... */ writeRegister(REG_HSTART, 0x13); writeRegister(REG_HSTOP, 0x01); writeRegister(REG_HREF, 0xb6); writeRegister(REG_VSTART, 0x02); writeRegister(REG_VSTOP, 0x7a); writeRegister(REG_VREF, 0x0a); writeRegister(REG_COM3, 0x00); writeRegister(REG_COM14, 0x00); /* Mystery scaling numbers */ writeRegister(0x70, 0x3a); writeRegister(0x71, 0x35); writeRegister(0x72, 0x11); writeRegister(0x73, 0xf0); writeRegister(0xa2, 0x02); writeRegister(REG_COM10, 0x00); /* Gamma curve values */ writeRegister(0x7a, 0x20); writeRegister(0x7b, 0x10); writeRegister(0x7c, 0x1e); writeRegister(0x7d, 0x35); writeRegister(0x7e, 0x5a); writeRegister(0x7f, 0x69); writeRegister(0x80, 0x76); writeRegister(0x81, 0x80); writeRegister(0x82, 0x88); writeRegister(0x83, 0x8f); writeRegister(0x84, 0x96); writeRegister(0x85, 0xa3); writeRegister(0x86, 0xaf); writeRegister(0x87, 0xc4); writeRegister(0x88, 0xd7); writeRegister(0x89, 0xe8); /* AGC and AEC parameters. Note we start by disabling those features, then turn them only after tweaking the values. */ writeRegister(REG_COM8, COM8_FASTAEC | COM8_AECSTEP | COM8_BFILT); writeRegister(REG_GAIN, 0x00); writeRegister(REG_AECH, 0x00); writeRegister(REG_COM4, 0x40); /* magic reserved bit */ writeRegister(REG_COM9, 0x18); /* 4x gain + magic rsvd bit */ writeRegister(REG_BD50MAX, 0x05); writeRegister(REG_BD60MAX, 0x07); writeRegister(REG_AEW, 0x95); writeRegister(REG_AEB, 0x33); writeRegister(REG_VPT, 0xe3); writeRegister(REG_HAECC1, 0x78); writeRegister(REG_HAECC2, 0x68); writeRegister(0xa1, 0x03); /* magic */ writeRegister(REG_HAECC3, 0xd8); writeRegister(REG_HAECC4, 0xd8); writeRegister(REG_HAECC5, 0xf0); writeRegister(REG_HAECC6, 0x90); writeRegister(REG_HAECC7, 0x94); writeRegister(REG_COM8, COM8_FASTAEC | COM8_AECSTEP | COM8_BFILT | COM8_AGC | COM8_AEC); /* Almost all of these are magic "reserved" values. */ writeRegister(REG_COM5, 0x61); writeRegister(REG_COM6, 0x4b); writeRegister(0x16, 0x02); writeRegister(REG_MVFP, 0x37); writeRegister(0x21, 0x02); writeRegister(0x22, 0x91); writeRegister(0x29, 0x07); writeRegister(0x33, 0x0b); writeRegister(0x35, 0x0b); writeRegister(0x37, 0x1d); writeRegister(0x38, 0x71); writeRegister(0x39, 0x2a); writeRegister(REG_COM12, 0x78); writeRegister(0x4d, 0x40); writeRegister(0x4e, 0x20); writeRegister(REG_GFIX, 0x00); writeRegister(0x6b, 0x4a); writeRegister(0x74, 0x10); writeRegister(0x8d, 0x4f); writeRegister(0x8e, 0x00); writeRegister(0x8f, 0x00); writeRegister(0x90, 0x00); writeRegister(0x91, 0x00); writeRegister(0x96, 0x00); writeRegister(0x9a, 0x00); writeRegister(0xb0, 0x84); writeRegister(0xb1, 0x0c); writeRegister(0xb2, 0x0e); writeRegister(0xb3, 0x82); writeRegister(0xb8, 0x0a); /* More reserved magic, some of which tweaks white balance */ writeRegister(0x43, 0x0a); writeRegister(0x44, 0xf0); writeRegister(0x45, 0x34); writeRegister(0x46, 0x58); writeRegister(0x47, 0x28); writeRegister(0x48, 0x3a); writeRegister(0x59, 0x88); writeRegister(0x5a, 0x88); writeRegister(0x5b, 0x44); writeRegister(0x5c, 0x67); writeRegister(0x5d, 0x49); writeRegister(0x5e, 0x0e); writeRegister(0x6c, 0x0a); writeRegister(0x6d, 0x55); writeRegister(0x6e, 0x11); writeRegister(0x6f, 0x9f); /* "9e for advance AWB" */ writeRegister(0x6a, 0x40); writeRegister(REG_BLUE, 0x40); writeRegister(REG_RED, 0x60); writeRegister(REG_COM8, COM8_FASTAEC | COM8_AECSTEP | COM8_BFILT | COM8_AGC | COM8_AEC | COM8_AWB); /* Matrix coefficients */ writeRegister(0x4f, 0x80); writeRegister(0x50, 0x80); writeRegister(0x51, 0x00); writeRegister(0x52, 0x22); writeRegister(0x53, 0x5e); writeRegister(0x54, 0x80); writeRegister(0x58, 0x9e); writeRegister(REG_COM16, COM16_AWBGAIN); writeRegister(REG_EDGE, 0x00); writeRegister(0x75, 0x05); writeRegister(0x76, 0xe1); writeRegister(0x4c, 0x00); writeRegister(0x77, 0x01); writeRegister(REG_COM13, 0xc3); writeRegister(0x4b, 0x09); writeRegister(0xc9, 0x60); writeRegister(REG_COM16, 0x38); writeRegister(0x56, 0x40); writeRegister(0x34, 0x11); writeRegister(REG_COM11, COM11_EXP | COM11_HZAUTO); writeRegister(0xa4, 0x88); writeRegister(0x96, 0x00); writeRegister(0x97, 0x30); writeRegister(0x98, 0x20); writeRegister(0x99, 0x30); writeRegister(0x9a, 0x84); writeRegister(0x9b, 0x29); writeRegister(0x9c, 0x03); writeRegister(0x9d, 0x4c); writeRegister(0x9e, 0x3f); writeRegister(0x78, 0x04); /* Extra-weird stuff. Some sort of multiplexor register */ writeRegister(0x79, 0x01); writeRegister(0xc8, 0xf0); writeRegister(0x79, 0x0f); writeRegister(0xc8, 0x00); writeRegister(0x79, 0x10); writeRegister(0xc8, 0x7e); writeRegister(0x79, 0x0a); writeRegister(0xc8, 0x80); writeRegister(0x79, 0x0b); writeRegister(0xc8, 0x01); writeRegister(0x79, 0x0c); writeRegister(0xc8, 0x0f); writeRegister(0x79, 0x0d); writeRegister(0xc8, 0x20); writeRegister(0x79, 0x09); writeRegister(0xc8, 0x80); writeRegister(0x79, 0x02); writeRegister(0xc8, 0xc0); writeRegister(0x79, 0x03); writeRegister(0xc8, 0x40); writeRegister(0x79, 0x05); writeRegister(0xc8, 0x30); writeRegister(0x79, 0x26); } void printData(int address, int data) { char temp[16]; snprintf(temp, sizeof(temp), "addr=%02X data=%02X", address, data); Serial.println(temp); } void receiveCommand(char *buff) { char c = '\0'; while (c != '\r' ) { if (Serial.available() > 0) { c = Serial.read(); Serial.print(c); *buff++ = c; } } *buff = '\0'; Serial.println(""); } void setup() { Serial.begin(2000000); Wire.begin(); printMenu(); setPinMode(); resetCamera(); initRegister(); } void loop() { int address = 0; int data = 0; char buff[256]; char *buff_ptr; char *cmd_ptr; char *arg1_ptr; char *arg2_ptr; const char DELIMITTER[] = " \t"; Serial.print("> "); receiveCommand(buff); // trim white space buff_ptr = buff; while ( (*buff_ptr == ' ') || (*buff_ptr == '\t') || (*buff_ptr == '\n') ) { buff_ptr++; } cmd_ptr = strtok(buff_ptr, DELIMITTER); switch (tolower(*cmd_ptr)) { case 'w': arg1_ptr = strtok(NULL, DELIMITTER); arg2_ptr = strtok(NULL, DELIMITTER); if ( (arg1_ptr != NULL) && (arg2_ptr != NULL) ) { address = hex2dec(arg1_ptr); data = hex2dec(arg2_ptr); printData(address, data); writeRegister(address, data); delay(10); } else { Serial.println("Syntax Error"); } break; case 'r': arg1_ptr = strtok(NULL, DELIMITTER); if (arg1_ptr != NULL) { address = hex2dec(arg1_ptr); data = readRegister(address); printData(address, data); delay(50); } else { Serial.println("Syntax Error"); } break; case 'f': arg1_ptr = strtok(NULL, DELIMITTER); if (arg1_ptr != NULL) { format = hex2dec(arg1_ptr); Serial.print("Format "); Serial.println(format); setDataFormat(); resetCamera(); initRegister(); } else { Serial.println("Syntax Error"); } break; case 'd': Serial.println("Dump"); initFifo(); readFifo(); break; case 's': Serial.println("Start"); isWriteEnable = true; break; case 'h': printHelp(); break; default: Serial.println("Syntax Error"); break; } Serial.println(""); } void printMenu() { Serial.println("------------------------------"); Serial.println(" OV7670 Camera Debugger"); Serial.println("------------------------------"); Serial.println(""); } void printHelp() { Serial.println("WRITE :w [address] [data]"); Serial.println("READ :r [address]"); Serial.println("FORMAT:f [mode] 0:VGA 1:QQVGA"); Serial.println("START :s"); Serial.println("DUMP :d"); Serial.println("HELP :h"); } int hex2dec(char *str) { return strtol(str, NULL, 16); }
次に,ov7670reg.h
/* VGA setting */ #define HSTART_VGA 0x13 #define HSTOP_VGA 0x01 #define HREF_VGA 0x36 #define VSTART_VGA 0x02 #define VSTOP_VGA 0x7a #define VREF_VGA 0x0a #define COM3_VGA 0x00 #define COM14_VGA 0x00 #define SCALING_XSC_VGA 0x3a #define SCALING_YSC_VGA 0x35 #define SCALING_DCWCTR_VGA 0x11 #define SCALING_PCLK_DIV_VGA 0xf0 #define SCALING_PCLK_DELAY_VGA 0x02 /* QQVGA setting */ #define HSTART_QQVGA 0x16 #define HSTOP_QQVGA 0x04 #define HREF_QQVGA 0xa4 #define VSTART_QQVGA 0x02 #define VSTOP_QQVGA 0x7a #define VREF_QQVGA 0x0a #define COM3_QQVGA 0x04 #define COM14_QQVGA 0x1a #define SCALING_XSC_QQVGA 0x3a #define SCALING_YSC_QQVGA 0x35 #define SCALING_DCWCTR_QQVGA 0x22 #define SCALING_PCLK_DIV_QQVGA 0xf2 #define SCALING_PCLK_DELAY_QQVGA 0x02 /* Registers */ #define REG_GAIN 0x00 /* Gain lower 8 bits (rest in vref) */ #define REG_BLUE 0x01 /* blue gain */ #define REG_RED 0x02 /* red gain */ #define REG_VREF 0x03 /* Pieces of GAIN, VSTART, VSTOP */ #define REG_COM1 0x04 /* Control 1 */ #define COM1_CCIR656 0x40 /* CCIR656 enable */ #define REG_BAVE 0x05 /* U/B Average level */ #define REG_GbAVE 0x06 /* Y/Gb Average level */ #define REG_AECHH 0x07 /* AEC MS 5 bits */ #define REG_RAVE 0x08 /* V/R Average level */ #define REG_COM2 0x09 /* Control 2 */ #define COM2_SSLEEP 0x10 /* Soft sleep mode */ #define REG_PID 0x0a /* Product ID MSB */ #define REG_VER 0x0b /* Product ID LSB */ #define REG_COM3 0x0c /* Control 3 */ #define COM3_SWAP 0x40 /* Byte swap */ #define COM3_SCALEEN 0x08 /* Enable scaling */ #define COM3_DCWEN 0x04 /* Enable downsamp/crop/window */ #define REG_COM4 0x0d /* Control 4 */ #define REG_COM5 0x0e /* All "reserved" */ #define REG_COM6 0x0f /* Control 6 */ #define REG_AECH 0x10 /* More bits of AEC value */ #define REG_CLKRC 0x11 /* Clocl control */ #define CLK_EXT 0x40 /* Use external clock directly */ #define CLK_SCALE 0x3f /* Mask for internal clock scale */ #define REG_COM7 0x12 /* Control 7 */ #define COM7_RESET 0x80 /* Register reset */ #define COM7_FMT_MASK 0x38 #define COM7_FMT_VGA 0x00 #define COM7_FMT_CIF 0x20 /* CIF format */ #define COM7_FMT_QVGA 0x10 /* QVGA format */ #define COM7_FMT_QCIF 0x08 /* QCIF format */ #define COM7_RGB 0x04 /* bits 0 and 2 - RGB format */ #define COM7_YUV 0x00 /* YUV */ #define COM7_BAYER 0x01 /* Bayer format */ #define COM7_PBAYER 0x05 /* "Processed bayer" */ #define REG_COM8 0x13 /* Control 8 */ #define COM8_FASTAEC 0x80 /* Enable fast AGC/AEC */ #define COM8_AECSTEP 0x40 /* Unlimited AEC step size */ #define COM8_BFILT 0x20 /* Band filter enable */ #define COM8_AGC 0x04 /* Auto gain enable */ #define COM8_AWB 0x02 /* White balance enable */ #define COM8_AEC 0x01 /* Auto exposure enable */ #define REG_COM9 0x14 /* Control 9 - gain ceiling */ #define REG_COM10 0x15 /* Control 10 */ #define COM10_HSYNC 0x40 /* HSYNC instead of HREF */ #define COM10_PCLK_HB 0x20 /* Suppress PCLK on horiz blank */ #define COM10_HREF_REV 0x08 /* Reverse HREF */ #define COM10_VS_LEAD 0x04 /* VSYNC on clock leading edge */ #define COM10_VS_NEG 0x02 /* VSYNC negative */ #define COM10_HS_NEG 0x01 /* HSYNC negative */ #define REG_HSTART 0x17 /* Horiz start high bits */ #define REG_HSTOP 0x18 /* Horiz stop high bits */ #define REG_VSTART 0x19 /* Vert start high bits */ #define REG_VSTOP 0x1a /* Vert stop high bits */ #define REG_PSHFT 0x1b /* Pixel delay after HREF */ #define REG_MIDH 0x1c /* Manuf. ID high */ #define REG_MIDL 0x1d /* Manuf. ID low */ #define REG_MVFP 0x1e /* Mirror / vflip */ #define MVFP_MIRROR 0x20 /* Mirror image */ #define MVFP_FLIP 0x10 /* Vertical flip */ #define REG_AEW 0x24 /* AGC upper limit */ #define REG_AEB 0x25 /* AGC lower limit */ #define REG_VPT 0x26 /* AGC/AEC fast mode op region */ #define REG_HSYST 0x30 /* HSYNC rising edge delay */ #define REG_HSYEN 0x31 /* HSYNC falling edge delay */ #define REG_HREF 0x32 /* HREF pieces */ #define REG_TSLB 0x3a /* lots of stuff */ #define TSLB_YLAST 0x04 /* UYVY or VYUY - see com13 */ #define REG_COM11 0x3b /* Control 11 */ #define COM11_NIGHT 0x80 /* NIght mode enable */ #define COM11_NMFR 0x60 /* Two bit NM frame rate */ #define COM11_HZAUTO 0x10 /* Auto detect 50/60 Hz */ #define COM11_50HZ 0x08 /* Manual 50Hz select */ #define COM11_EXP 0x02 #define REG_COM12 0x3c /* Control 12 */ #define COM12_HREF 0x80 /* HREF always */ #define REG_COM13 0x3d /* Control 13 */ #define COM13_GAMMA 0x80 /* Gamma enable */ #define COM13_UVSAT 0x40 /* UV saturation auto adjustment */ #define COM13_UVSWAP 0x01 /* V before U - w/TSLB */ #define REG_COM14 0x3e /* Control 14 */ #define COM14_DCWEN 0x10 /* DCW/PCLK-scale enable */ #define REG_EDGE 0x3f /* Edge enhancement factor */ #define REG_COM15 0x40 /* Control 15 */ #define COM15_R10F0 0x00 /* Data range 10 to F0 */ #define COM15_R01FE 0x80 /* 01 to FE */ #define COM15_R00FF 0xc0 /* 00 to FF */ #define COM15_RGB444 0x10 /* RGB444 output */ #define COM15_RGB565 0x10 /* RGB565 output */ #define COM15_RGB555 0x30 /* RGB555 output */ #define REG_COM16 0x41 /* Control 16 */ #define COM16_AWBGAIN 0x08 /* AWB gain enable */ #define REG_COM17 0x42 /* Control 17 */ #define COM17_AECWIN 0xc0 /* AEC window - must match COM4 */ #define COM17_CBAR 0x08 /* DSP Color bar */ /* * This matrix defines how the colors are generated, must be * tweaked to adjust hue and saturation. * * Order: v-red, v-green, v-blue, u-red, u-green, u-blue * * They are nine-bit signed quantities, with the sign bit * stored in 0x58. Sign for v-red is bit 0, and up from there. */ #define REG_CMATRIX_BASE 0x4f #define CMATRIX_LEN 6 #define REG_CMATRIX_SIGN 0x58 #define REG_BRIGHT 0x55 /* Brightness */ #define REG_CONTRAS 0x56 /* Contrast control */ #define REG_GFIX 0x69 /* Fix gain control */ #define REG_DBLV 0x6b /* PLL control an debugging */ #define DBLV_BYPASS 0x00 /* Bypass PLL */ #define DBLV_X4 0x01 /* clock x4 */ #define DBLV_X6 0x10 /* clock x6 */ #define DBLV_X8 0x11 /* clock x8 */ #define REG_SCALING_XSC 0x70 #define REG_SCALING_YSC 0x71 #define REG_SCALING_DCWCTR 0x72 #define REG_SCALING_PCLK_DIV 0x73 #define REG_REG76 0x76 /* OV's name */ #define R76_BLKPCOR 0x80 /* Black pixel correction enable */ #define R76_WHTPCOR 0x40 /* White pixel correction enable */ #define REG_RGB444 0x8c /* RGB 444 control */ #define RGB444_ENABLE 0x02 /* Turn on RGB444, overrides 5x5 */ #define RGB444_DISABLE 0x00 /* Turn off RGB444, overrides 5x5 */ #define RGB444_BGRX 0x01 /* Empty nibble at end */ #define RGB444_XBGR 0x00 /* Empty nibble at start */ #define REG_HAECC1 0x9f /* Hist AEC/AGC control 1 */ #define REG_HAECC2 0xa0 /* Hist AEC/AGC control 2 */ #define REG_SCALING_PCLK_DELAY 0xa2 #define REG_BD50MAX 0xa5 /* 50hz banding step limit */ #define REG_HAECC3 0xa6 /* Hist AEC/AGC control 3 */ #define REG_HAECC4 0xa7 /* Hist AEC/AGC control 4 */ #define REG_HAECC5 0xa8 /* Hist AEC/AGC control 5 */ #define REG_HAECC6 0xa9 /* Hist AEC/AGC control 6 */ #define REG_HAECC7 0xaa /* Hist AEC/AGC control 7 */ #define REG_BD60MAX 0xab /* 60hz banding step limit */
次にProcessing側のコード.こちらは,CameraControl.pde,Button.pde,CheckBox.pde,Command.pde,TextBox.pdeから構成されているので,順に示そう.あと,Font(.vlwファイル)は別途生成してください.
では,CameraControl.pde.
import processing.serial.*; import java.awt.*; Serial port; PFont fontButton; PFont fontCheckBox; PFont fontData; Button startButton; Button dumpButton; Button readButton; Button writeButton; Button formatVGAButton; Button formatQQVGAButton; Button continuousButton; Button singleButton; CheckBox saveCheckBox; String readResult = "addr= data= "; String writeResult = "addr= data= "; String csStatus = ""; TextBox readAddress; TextBox writeAddress; TextBox writeData; TextBox intervalSec; PImage img; static final int FRAME_RATE = 10; int frame_counter = 0; static final int TIMEOUT_THRESHOLD_SEC = 30; int timeout_counter = 0; static final int VGA_WIDTH = 640; static final int VGA_HEIGHT = 480; static final int QQVGA_WIDTH = 160; static final int QQVGA_HEIGHT = 120; int width = VGA_WIDTH; int height = VGA_HEIGHT; int[] r = new int[VGA_WIDTH * VGA_HEIGHT]; int[] g = new int[VGA_WIDTH * VGA_HEIGHT]; int[] b = new int[VGA_WIDTH * VGA_HEIGHT]; static final int VGA = 0; static final int QQVGA = 1; int format = VGA; static final int BAYER_RGGB = 0; static final int BAYER_GRBG = 1; static final int BAYER_GBRG = 2; static final int BAYER_BGGR = 3; static final int MODE_INIT = -1; static final int MODE_NONE = 0; static final int MODE_START = 1; static final int MODE_DUMP = 2; static final int MODE_READ = 3; static final int MODE_WRITE = 4; static final int MODE_FORMAT = 5; int cmd_mode = MODE_INIT; boolean is_dump_ready = false; boolean isContinuousMode = true; Command read_cmd; Command write_cmd; Command format_cmd; Command start_cmd; Command dump_cmd; void drawButtons() { startButton.draw(); dumpButton.draw(); readButton.draw(); writeButton.draw(); formatVGAButton.draw(); formatQQVGAButton.draw(); continuousButton.draw(); singleButton.draw(); } void drawCheckBox() { saveCheckBox.draw(); } void setup() { println(Serial.list()); String arduinoPort = Serial.list()[2]; port = new Serial(this, arduinoPort, 2000000); port.bufferUntil('\n'); frameRate(FRAME_RATE); size(880, 560); colorMode(RGB, 255); fontButton = loadFont("Helvetica-Bold-14.vlw"); fontCheckBox = loadFont("Monaco-12.vlw"); fontData = loadFont("Monaco-12.vlw"); startButton = new Button(680+10, 10, 80, 24, "Start"); dumpButton = new Button(680+10, 40, 80, 24, "Dump"); readButton = new Button(680+10, 100, 80, 24, "Read"); writeButton = new Button(680+10, 160, 80, 24, "Write"); formatVGAButton = new Button(680+10, 240, 80, 24, "VGA"); formatQQVGAButton = new Button(680+10, 270, 80, 24, "QQVGA"); continuousButton = new Button(680+10, 350, 30, 24, "C"); singleButton = new Button(680+50, 350, 30, 24, "S"); saveCheckBox = new CheckBox(680+10, 420, false, "Save Image"); setLayout(null); readAddress = new TextBox(680+100, 100, 30, 24, "01"); writeAddress = new TextBox(680+100, 160, 30, 24, "80"); writeData = new TextBox(680+135, 160, 30, 24, "ff"); intervalSec = new TextBox(680+10, 440, 30, 24, "0"); for (int i = 0; i < width*height; i++) { r[i] = 0; g[i] = 90; b[i] = 102; } img = createImage(width, height, RGB); img.loadPixels(); for (int i = 0; i < img.pixels.length; i++) { img.pixels[i] = color(r[i], g[i], b[i]); } read_cmd = new Command(); write_cmd = new Command(); format_cmd = new Command(); start_cmd = new Command(); dump_cmd = new Command(); } void setImageFormat() { switch (format) { case VGA: width = VGA_WIDTH; height = VGA_HEIGHT; img = createImage(width, height, RGB); img.loadPixels(); break; case QQVGA: width = QQVGA_WIDTH; height = QQVGA_HEIGHT; img = createImage(width, height, RGB); img.loadPixels(); break; default: ; break; } } boolean isCmdRunning() { boolean is_cmd_running; if (cmd_mode == MODE_NONE) { timeout_counter = 0; is_cmd_running = false; } else { timeout_counter++; if (timeout_counter > TIMEOUT_THRESHOLD_SEC * FRAME_RATE) { timeout_counter = 0; is_dump_ready = false; cmd_mode = MODE_NONE; is_cmd_running = false; println("Timeout detect!"); } else { is_cmd_running = true; } } return is_cmd_running; } void execStartCmd() { cmd_mode = MODE_START; port.write("s\r\n"); } void execDumpCmd() { cmd_mode = MODE_DUMP; port.write("d\r\n"); } void execReadCmd(String address) { cmd_mode = MODE_READ; port.write("r " + address + "\r\n"); } void execWriteCmd(String address, String data) { cmd_mode = MODE_WRITE; port.write("w " + address + " " + data + "\r\n"); } void execFormatCmd() { cmd_mode = MODE_FORMAT; format = format_cmd.format; setImageFormat(); port.write("f " + format + "\r\n"); } void draw() { background(196); drawButtons(); drawCheckBox(); textFont(fontData, 12); textAlign(LEFT); fill(255); text(readResult, 680+10, 140); text(writeResult, 680+10, 200); if (isContinuousMode == true) { csStatus = "continuous"; } else { csStatus = "single"; } text(csStatus, 680+90, 370); text("sec (StartInterval)", 680+45, 460); image(img, 10, 10, 640, 480); if (isCmdRunning() == true) { return; } if (read_cmd.request == true) { execReadCmd(read_cmd.address); read_cmd.request = false; } else if (write_cmd.request == true) { execWriteCmd(write_cmd.address, write_cmd.data); write_cmd.request = false; } else if (format_cmd.request == true) { execFormatCmd(); format_cmd.request = false; } else if (start_cmd.request == true) { execStartCmd(); start_cmd.request = false; if (isContinuousMode == true) { dump_cmd.request = true; } } else if (dump_cmd.request == true) { execDumpCmd(); dump_cmd.request = false; } else { if (isContinuousMode == true) { int interval_sec = int(intervalSec.getText()); if (frame_counter > interval_sec * FRAME_RATE) { frame_counter = 0; start_cmd.request = true; } } else { frame_counter = 0; } } frame_counter++; } void mouseClicked() { if (startButton.isMouseOn()) { start_cmd.request = true; } if (dumpButton.isMouseOn()) { dump_cmd.request = true; } if (readButton.isMouseOn()) { read_cmd.address = readAddress.getText(); read_cmd.request = true; } if (writeButton.isMouseOn()) { write_cmd.address = writeAddress.getText(); write_cmd.data = writeData.getText(); write_cmd.request = true; } if (formatVGAButton.isMouseOn()) { format_cmd.format = VGA; format_cmd.request = true; } if (formatQQVGAButton.isMouseOn()) { format_cmd.format = QQVGA; format_cmd.request = true; } if (continuousButton.isMouseOn()) { isContinuousMode = true; } if (singleButton.isMouseOn()) { isContinuousMode = false; } if (saveCheckBox.isMouseOn()) { saveCheckBox.click(); } } void updateRGBData(String string) { switch (format) { case VGA: updateRGBDataBayerVGA(BAYER_RGGB, string); break; case QQVGA: updateRGBDataRGB565QQVGA(string); break; default: ; break; } for (int i = 0; i < width * height; i++) { img.pixels[i] = color(r[i], g[i], b[i]); } img.updatePixels(); if (saveCheckBox.is_checked) { img.save("image/" + getDateString()); } } /* * BayerPattern * BAYER_RGGB BAYER_GRBG BAYER_GBRG BAYER_BGGR * * RGRGRGRGRG GRGRGRGRGR GBGBGBGBGB BGBGBGBGBG * GBGBGBGBGB BGBGBGBGBG RGRGRGRGRG GRGRGRGRGR * RGRGRGRGRG GRGRGRGRGR GBGBGBGBGB BGBGBGBGBG * GBGBGBGBGB BGBGBGBGBG RGRGRGRGRG GRGRGRGRGR */ void updateRGBDataBayerVGA(int pattern, String string) { int d0, d1, d2, d3, d4, d5, d6, d7, d8; int i0, i1, i2, i3, i4, i5, i6, i7, i8; /* * data index * d0 d1 d2 i0 i1 i2 * d3 d4 d5 i3 i4 i5 * d6 d7 d8 i6 i7 i8 */ for (int y = 0; y < height; y++) { for (int x = 0; x < width; x++) { i0 = (x - 1) + (y - 1) * width; i1 = (x ) + (y - 1) * width; i2 = (x + 1) + (y - 1) * width; i3 = (x - 1) + (y ) * width; i4 = (x ) + (y ) * width; i5 = (x + 1) + (y ) * width; i6 = (x - 1) + (y + 1) * width; i7 = (x ) + (y + 1) * width; i8 = (x + 1) + (y + 1) * width; if (y == 0) { i0 = i6; i1 = i7; i2 = i8; } else if (y == height - 1) { i6 = i0; i7 = i1; i8 = i2; } if (x == 0) { i0 = i2; i3 = i5; i6 = i8; } else if (x == width - 1) { i2 = i0; i5 = i3; i8 = i6; } d0 = unhex( string.substring(i0 * 2, i0 * 2 + 2) ); d1 = unhex( string.substring(i1 * 2, i1 * 2 + 2) ); d2 = unhex( string.substring(i2 * 2, i2 * 2 + 2) ); d3 = unhex( string.substring(i3 * 2, i3 * 2 + 2) ); d4 = unhex( string.substring(i4 * 2, i4 * 2 + 2) ); d5 = unhex( string.substring(i5 * 2, i5 * 2 + 2) ); d6 = unhex( string.substring(i6 * 2, i6 * 2 + 2) ); d7 = unhex( string.substring(i7 * 2, i7 * 2 + 2) ); d8 = unhex( string.substring(i8 * 2, i8 * 2 + 2) ); /* * x/y * BAYER_RGGB 0/0 1/0 0/1 1/1 * BAYER_GRBG 1/0 0/0 1/1 0/1 * BAYER_GBRG 0/1 1/1 0/0 1/0 * BAYER_BGGR 1/1 0/1 1/0 0/0 data index * BGB GBG GRG RGR d0 d1 d2 i0 i1 i2 * Pixel GRG RGR BGB GBG d3 d4 d5 i3 i4 i5 * BGB GBG GRG RGR d6 d7 d8 i6 i7 i8 */ if ( ( (pattern == BAYER_RGGB) && ( ((x&1) == 0) && ((y&1) == 0) ) ) || ( (pattern == BAYER_GRBG) && ( ((x&1) == 1) && ((y&1) == 0) ) ) || ( (pattern == BAYER_GBRG) && ( ((x&1) == 0) && ((y&1) == 1) ) ) || ( (pattern == BAYER_BGGR) && ( ((x&1) == 1) && ((y&1) == 1) ) ) ) { r[i4] = d4; g[i4] = (d1 + d3 + d5 + d7) / 4; b[i4] = (d0 + d2 + d6 + d8) / 4; } else if ( ( (pattern == BAYER_RGGB) && ( ((x&1) == 1) && ((y&1) == 0) ) ) || ( (pattern == BAYER_GRBG) && ( ((x&1) == 0) && ((y&1) == 0) ) ) || ( (pattern == BAYER_GBRG) && ( ((x&1) == 1) && ((y&1) == 1) ) ) || ( (pattern == BAYER_BGGR) && ( ((x&1) == 0) && ((y&1) == 1) ) ) ) { r[i4] = (d3 + d5) / 2; g[i4] = (d4 * 4 + d0 + d2 + d6 + d8) / 8; b[i4] = (d1 + d7) / 2; } else if ( ( (pattern == BAYER_RGGB) && ( ((x&1) == 0) && ((y&1) == 1) ) ) || ( (pattern == BAYER_GRBG) && ( ((x&1) == 1) && ((y&1) == 1) ) ) || ( (pattern == BAYER_GBRG) && ( ((x&1) == 0) && ((y&1) == 0) ) ) || ( (pattern == BAYER_BGGR) && ( ((x&1) == 1) && ((y&1) == 0) ) ) ) { r[i4] = (d1 + d7) / 2; g[i4] = (d4 * 4 + d0 + d2 + d6 + d8) / 8; b[i4] = (d3 + d5) / 2; } else if ( ( (pattern == BAYER_RGGB) && ( ((x&1) == 1) && ((y&1) == 1) ) ) || ( (pattern == BAYER_GRBG) && ( ((x&1) == 0) && ((y&1) == 1) ) ) || ( (pattern == BAYER_GBRG) && ( ((x&1) == 1) && ((y&1) == 0) ) ) || ( (pattern == BAYER_BGGR) && ( ((x&1) == 0) && ((y&1) == 0) ) ) ) { r[i4] = (d0 + d2 + d6 + d8) / 4; g[i4] = (d1 + d3 + d5 + d7) / 4; b[i4] = d4; } } } } void updateRGBDataRGB444QQVGA(String string) { // xBGR for (int i = 0; i < width * height; i++) { r[i] = unhex(String.valueOf(string.charAt(4*i+3))) *16; g[i] = unhex(String.valueOf(string.charAt(4*i+2))) *16; b[i] = unhex(String.valueOf(string.charAt(4*i+1))) *16; } } void updateRGBDataRGB565QQVGA(String string) { /* * [ dh ] [ dl ] * 7654 3210 7654 3210 * [ R ][ G ][ B ] */ for (int i = 0; i < width * height; i++) { int dl = unhex( string.substring(i * 4, i * 4 + 2 ) ); int dh = unhex( string.substring(i * 4 + 2, i * 4 + 2 + 2) ); r[i] = (dh & 0xF8); g[i] = ((dh & 0x07) << 5) | ((dl & 0xE0) >> 3); b[i] = (dl & 0x1F) << 3; } } void serialEvent(Serial port) { String inString = port.readStringUntil('\n'); if (inString != null) { inString = trim(inString); // println(inString); switch (cmd_mode) { case MODE_INIT: if (inString.equals("OV7670 Camera Debugger") == true) { cmd_mode = MODE_NONE; } break; case MODE_START: if (inString.equals("Start") == true) { cmd_mode = MODE_NONE; } break; case MODE_DUMP: if (is_dump_ready == true) { updateRGBData(inString); is_dump_ready = false; cmd_mode = MODE_NONE; } if (inString.equals("Dump") == true) { is_dump_ready = true; } break; case MODE_READ: if (inString.length() >= 5 && inString.substring(0, 5).equals("addr=") == true) { readResult = new String(inString); cmd_mode = MODE_NONE; } break; case MODE_WRITE: if (inString.length() >= 5 && inString.substring(0, 5).equals("addr=") == true) { writeResult = new String(inString); cmd_mode = MODE_NONE; } break; case MODE_FORMAT: if (inString.length() >= 6 && inString.substring(0, 6).equals("Format") == true) { cmd_mode = MODE_NONE; } break; default: break; } } } String getDateString() { int year = year(); int month = month(); int day = day(); int hour = hour(); int minute = minute(); int second = second(); String date = nf(year, 4) + nf(month, 2) + nf(day, 2) + nf(hour, 2) + nf(minute, 2) + nf(second, 2); return date; }
Button.pde.
class Button { int x; int y; int w; int h; String str; Button(int x, int y, int w, int h, String str) { this.x = x; this.y = y; this.w = w; this.h = h; this.str = str; } boolean isMouseOn() { return ( (this.x <= mouseX && mouseX <= this.x + this.w) && (this.y <= mouseY && mouseY <= this.y + this.h) ); } void draw() { noStroke(); color c = color(0, 255, 0); float alpha = 0.8; if (this.isMouseOn()) { fill(c); } else { fill(red(c) * alpha, green(c) * alpha, blue(c) * alpha, alpha(c)); } rect(this.x, this.y, this.w, this.h); int fontSize = 14; textFont(fontButton, fontSize); textAlign(CENTER); fill(0); text(this.str, this.x + this.w/2, this.y + this.h - fontSize/2); } };
CheckBox.pde.
class CheckBox { int x; int y; boolean is_checked; String str; private static final int SIZE = 10; CheckBox(int x, int y, boolean is_checked, String str) { this.x = x; this.y = y; this.is_checked = is_checked; this.str = str; } void draw() { stroke(255); color c = color(0, 255, 0); float alpha = 0.8; if (this.isMouseOn()) { fill(c); } else { fill(red(c) * alpha, green(c) * alpha, blue(c) * alpha, alpha(c)); } rect(x, y, SIZE, SIZE); if (is_checked) { line(x, y, x + SIZE, y + SIZE); line(x, y + SIZE, x + SIZE, y ); } int fontSize = 12; textFont(fontCheckBox, fontSize); textAlign(LEFT, CENTER); fill(255); text(this.str, this.x + SIZE * 2, this.y + SIZE/2); } void click() { if (isMouseOn()) { is_checked = !is_checked; } } boolean isMouseOn() { return ( (this.x <= mouseX && mouseX <= this.x + SIZE) && (this.y <= mouseY && mouseY <= this.y + SIZE) ); } };
Command.pde.
class Command { boolean request; String address; String data; int format; Command() { request = false; address = ""; data = ""; format = VGA; } };
TextBox.pde.
class TextBox { TextField tf; TextBox(int x, int y, int w, int h, String str) { tf = new TextField(str); tf.setFont(new Font("monaco", Font.PLAIN, 12)); tf.setBounds(x, y, w, h); add(tf); } String getText() { return tf.getText(); } };
Processingで作ったアプリの使い方は...
Arduinoがつながっている状態で起動すると,VGAで画像の連続取得が自動的に動きます.
あとは以下の画像内の説明を見ながら使ってみてください.
では,お試しあれ.
Arduinoでモーションセンサを使ってみる [Arduino]
Arduinoでモーションセンサを使ってみる.
使ったのは,Panasonicの焦電型赤外線センサのうち,NaPiOn (ナピオン) シリーズのAMN31112だ.
このセンサは,電源とGNDと出力ピンしかないので,接続も簡単だ.
Arduinoとの接続はこんな感じ.
出力ピンをプルダウンしなければいけないというのが唯一の注意点.
Arduinoのスケッチはこんな感じ.
これでモーションセンサが人体検出したら,Arduino上のLEDが点灯する.
モーションセンサ側がいろいろやってくれるので,Arduino側からは単に出力を見るだけでOK.
とても簡単だ.
お試しあれ.
使ったのは,Panasonicの焦電型赤外線センサのうち,NaPiOn (ナピオン) シリーズのAMN31112だ.
このセンサは,電源とGNDと出力ピンしかないので,接続も簡単だ.
Arduinoとの接続はこんな感じ.
出力ピンをプルダウンしなければいけないというのが唯一の注意点.
Arduinoのスケッチはこんな感じ.
const int LED = 13; const int PIR = 2; void setup() { pinMode(LED, OUTPUT); pinMode(PIR, INPUT); } void loop() { if (digitalRead(PIR) == HIGH) { digitalWrite(LED, HIGH); } else { digitalWrite(LED, LOW); } delay(200); }
これでモーションセンサが人体検出したら,Arduino上のLEDが点灯する.
モーションセンサ側がいろいろやってくれるので,Arduino側からは単に出力を見るだけでOK.
とても簡単だ.
お試しあれ.
タグ:モーションセンサ
Arduinoに照度センサをつないでみる [Arduino]
手元のジャンクにI2C接続の照度センサがあったのでArduinoにつないでみた.
照度センサは,ROHMのBH1751FVI.I2Cで制御するタイプだ.
で,ArduinoとはIOの電圧が違うので直結できない.なので,秋月のI2Cバス用双方向電圧レベル変換モジュール(PCA9306)を経由して接続してやる.
こんな感じ.
間にI2Cのレベル変換を挟んでいるだけで,基本的には素直に接続なので悩むことはないと思う.
で,Arduino側のスケッチはこんな感じ.
簡単すぎる.これでシリアルに照度値が出力されるようになる.
Arduinoってなにかちょっと試すのにホント便利だなぁ.
照度センサは,ROHMのBH1751FVI.I2Cで制御するタイプだ.
で,ArduinoとはIOの電圧が違うので直結できない.なので,秋月のI2Cバス用双方向電圧レベル変換モジュール(PCA9306)を経由して接続してやる.
こんな感じ.
間にI2Cのレベル変換を挟んでいるだけで,基本的には素直に接続なので悩むことはないと思う.
で,Arduino側のスケッチはこんな感じ.
#include <Wire.h> const int SensorAddress = 0x23; const int CommandHResolutionSingle = 0x20; const int ReadDataNum = 2; void setup() { Wire.begin(); Serial.begin(9600); } void loop() { Wire.beginTransmission(SensorAddress); Wire.write(CommandHResolutionSingle); Wire.endTransmission(); Wire.requestFrom(SensorAddress, ReadDataNum); while (Wire.available() < ReadDataNum); unsigned int h = Wire.read(); unsigned int l = Wire.read(); double lx = convertSensorDataToLx(h, l); Serial.println(lx); delay(500); } double convertSensorDataToLx(unsigned int high, unsigned int lo) { double result = (high * 256u + lo) / 1.2; return result; }
簡単すぎる.これでシリアルに照度値が出力されるようになる.
Arduinoってなにかちょっと試すのにホント便利だなぁ.
秋月電子のATmega168/328マイコンボードキットを組み立ててみる [Arduino]
Arduino 互換ボードになるATmega168/328マイコンボードキットを組み立ててみた.AE-ATmegaというやつだ.
組み立ては難しくなくて,説明書の順番通り半田付けしていけばOK.
ただ,USBシリアル変換モジュール(AE-UM232R)は使い回したいので直接半田付けはしなかった.本当はICソケットを使うのがいいんだろうけど,手元になかったのでやむなくピンソケットをつけてみた.
なので,USBシリアル変換のところだけやたら高さが高くなってしまった.
で,組み立てが終わったらArduino化するためにブートローダーを書き込む.
ネットで調べると,ブートローダーの書き込みはいくつかの方法があるようだが,手元にあるArduino UNOを使って書き込むことができるようなので,それを試してみた.ArduinoISPと呼ぶようである.
方法は,本家「Arduino - ArduinoISP」,もしくはその日本語訳サイト「Arduino を AVR プログラマ(ISP: In-System Programmer)として使う」に情報がある.
で,接続はこんな感じでOK.
あとは書き込み手順通りに進める.
ターゲットボードは,AE-Atmegaはないので,「Arduino Diecimila or Duemilanove w/ ATmega168」を選択する.
で,書き込むのだが,残念ながらこの方法では書き込めない.
こんなエラーが出る.
調べてみると,どうやら「Arduino Diecimila or Duemilanove w/ ATmega168」はATmega168なのだが,AE-ATmegaに載っているのはATmega168Pなので,チェックで引っかかるようだ.
というわけで,このままではブートローダーを書き込めないので,なんとかATmega168として認識されるようにごまかしてみた.
以下のやり方はMac環境化での話になるが,ほかの環境でも同様のことができると思う.
では,方法だが,とても簡単.
/Applications/Arduino.app/Contents/Resources/Java/hardware/tools/avr/etc/下にある,avrdude.confを編集する.
で,編集する部分を一部抜粋したのが以下.
このうち,
つまり,
これで書き込める.
書き込みが終わったら変更を元に戻しておくことを忘れずに.
ここまできたら,もう普通にArduinoとして使える.
サンプルスケッチを書き込んでみて,動作を確認してみたが,問題なく動作した.
お試しあれ.
組み立ては難しくなくて,説明書の順番通り半田付けしていけばOK.
ただ,USBシリアル変換モジュール(AE-UM232R)は使い回したいので直接半田付けはしなかった.本当はICソケットを使うのがいいんだろうけど,手元になかったのでやむなくピンソケットをつけてみた.
なので,USBシリアル変換のところだけやたら高さが高くなってしまった.
で,組み立てが終わったらArduino化するためにブートローダーを書き込む.
ネットで調べると,ブートローダーの書き込みはいくつかの方法があるようだが,手元にあるArduino UNOを使って書き込むことができるようなので,それを試してみた.ArduinoISPと呼ぶようである.
方法は,本家「Arduino - ArduinoISP」,もしくはその日本語訳サイト「Arduino を AVR プログラマ(ISP: In-System Programmer)として使う」に情報がある.
で,接続はこんな感じでOK.
あとは書き込み手順通りに進める.
ターゲットボードは,AE-Atmegaはないので,「Arduino Diecimila or Duemilanove w/ ATmega168」を選択する.
で,書き込むのだが,残念ながらこの方法では書き込めない.
こんなエラーが出る.
avrdude: Expected signature for ATMEGA168 is 1E 94 06 Double check chip, or use -F to override this check.
調べてみると,どうやら「Arduino Diecimila or Duemilanove w/ ATmega168」はATmega168なのだが,AE-ATmegaに載っているのはATmega168Pなので,チェックで引っかかるようだ.
というわけで,このままではブートローダーを書き込めないので,なんとかATmega168として認識されるようにごまかしてみた.
以下のやり方はMac環境化での話になるが,ほかの環境でも同様のことができると思う.
では,方法だが,とても簡単.
/Applications/Arduino.app/Contents/Resources/Java/hardware/tools/avr/etc/下にある,avrdude.confを編集する.
で,編集する部分を一部抜粋したのが以下.
#------------------------------------------------------------ # ATmega168 #------------------------------------------------------------ part id = "m168"; desc = "ATMEGA168"; has_debugwire = yes; flash_instr = 0xB6, 0x01, 0x11; eeprom_instr = 0xBD, 0xF2, 0xBD, 0xE1, 0xBB, 0xCF, 0xB4, 0x00, 0xBE, 0x01, 0xB6, 0x01, 0xBC, 0x00, 0xBB, 0xBF, 0x99, 0xF9, 0xBB, 0xAF; stk500_devcode = 0x86; # avr910_devcode = 0x; signature = 0x1e 0x94 0x06;
このうち,
を168P用に一時的に変更する.signature = 0x1e 0x94 0x06;
つまり,
にしてやる.signature = 0x1e 0x94 0x0b;
これで書き込める.
書き込みが終わったら変更を元に戻しておくことを忘れずに.
ここまできたら,もう普通にArduinoとして使える.
サンプルスケッチを書き込んでみて,動作を確認してみたが,問題なく動作した.
お試しあれ.
タグ:AE-ATmega
ov7670+Arduino+ProcessingでTimelapseをやってみる [Arduino]
長らく放置していたCMOSカメラのov7670だが,とりあえず,秋月電子通商のArduino用ユニバーサル基板に組んでみた.
こんな感じ.
で,Arduino側は前はQQVGAだったけど,VGAで画像取得できるようにして,あとProcessingで画像表示用にこんなの作ってみた.
これで画像を保存できるようにしたので,画像を撮りまくってTimelapseをやってみた.
その動画がこれ.同じような動画ばっかりだけど...
かなり簡単にできた.Processingが画像を動画にしてくれるので楽だ.
ArduinoとProcessingのコードはかなり適当な作りなので,もし要望があったら公開しようと思う.
2015.05.30追記
ArduinoとProcessingのコードはこちら.
久しぶりにov7670+Arduino+Processingを動かしてみる
お試しあれ.
こんな感じ.
で,Arduino側は前はQQVGAだったけど,VGAで画像取得できるようにして,あとProcessingで画像表示用にこんなの作ってみた.
これで画像を保存できるようにしたので,画像を撮りまくってTimelapseをやってみた.
その動画がこれ.同じような動画ばっかりだけど...
かなり簡単にできた.Processingが画像を動画にしてくれるので楽だ.
ArduinoとProcessingのコードはかなり適当な作りなので,もし要望があったら公開しようと思う.
2015.05.30追記
ArduinoとProcessingのコードはこちら.
久しぶりにov7670+Arduino+Processingを動かしてみる
お試しあれ.
タグ:TIMELAPSE
ArduinoでCMOSカメラの画像を取得してみる [Arduino]
先日,ArduinoとCMOSカメラを接続して動かしてみたので,いよいよ画像を取得してみる.
というわけで,早速だがArduinoのスケッチを示そう.
ファイルは2つあって,ov7670.inoとov7670reg.hだ.
まずはov7670.ino
次にov7670reg.h
これをArduinoに書き込めばOK.
いろいろwebで調べて,とりあえずQQVGAのRGB444で画像取得する例がいくつか見つかったので,それで試してる.
使い方は,シリアルモニタを開いて,
sを入力すると撮影,dで画像データのダンプが行われる.hで簡単なヘルプ(というほどのものでもないが)が表示される.なので,まぁスケッチを見ながらいろいろ試してみてくださいな.
画像データは,QQVGAのRGB444で動かしているので,ダンプすると160x120=19200画素のデータが,1画素あたり2バイト出力される.なので,トータルとしては38400バイトになる.
データの並びは,xBGRで出力される.なお,160画素ごとに改行を入れて出力される.改行を除去すると,1バイトあたり2文字でダンプされるので,結果として,76800文字のデータが得られることになる.
さて,データだけあってもつまらないので,Processingで画像を表示してみる.
Processingのコードは,こんな感じ.
ダンプしたデータは,captureData.txtとしてProcessingのコードと同じディレクトリに保存してくださいな.
で,表示されるサンプル画像はこんな感じ.
ま,QQVGなのと,RGB各色4ビットしかないので,画質はそれなりですな.
あと,水平方向の位相がちょっとずれてるようなのと,電源投入一発目の画像がおかしいことがあるんだけど...
ま,お試しあれ.
参考サイト
いっぱいあって,いっぱい参考にした.特に以下の2つを参考にしたけど,ほかにもGoogleでov7670で検索してヒットするサイトは軒並みチェックした.先人に感謝.
トラ技 頒布カメラB(OV7670 FIFO AL422B)の動作確認 | mbed
HR2_blog: mbedでトラ技3月号の頒布カメラBを使う
2014.12.15追記
サンプル画像の元データ(captureData.txt)はこんな感じ.captureData.txt.zip.jpg
So-netのブログはテキストファイルのアップロードができないので,ZIP圧縮して,拡張子に.jpgをつけて無理矢理アップロードしてる.なので,ダウンロードしたら.jpgを消してZIPファイルとして展開してくださいな.
というわけで,早速だがArduinoのスケッチを示そう.
ファイルは2つあって,ov7670.inoとov7670reg.hだ.
まずはov7670.ino
#include <Wire.h> #include "ov7670reg.h" // Pin const int WEN = 14; // PC0 const int RRST = 15; // PC1 const int OE = 16; // PC2 const int RCLK = 17; // PC3 const int VSYNC = 2; // PD2 INT0 const int D0 = 8; // PB0 const int D1 = 9; // PB1 const int D2 = 10; // PB2 const int D3 = 11; // PB3 const int D4 = 4; // PD4 const int D5 = 5; // PD5 const int D6 = 6; // PD6 const int D7 = 7; // PD7 boolean isWriteEnable = false; void setPinMode() { pinMode(WEN, OUTPUT); pinMode(RRST, OUTPUT); pinMode(OE, OUTPUT); pinMode(RCLK, OUTPUT); pinMode(D0, INPUT ); pinMode(D1, INPUT ); pinMode(D2, INPUT ); pinMode(D3, INPUT ); pinMode(D4, INPUT ); pinMode(D5, INPUT ); pinMode(D6, INPUT ); pinMode(D7, INPUT ); attachInterrupt(0, setWriteEnable, FALLING); digitalWrite(WEN, LOW); digitalWrite(RRST, HIGH); digitalWrite(OE, HIGH); digitalWrite(RCLK, HIGH); } void setWriteEnable() { if (isWriteEnable) { digitalWrite(WEN, HIGH); isWriteEnable = false; } else { digitalWrite(WEN, LOW); } } void initFifo() { digitalWrite(RCLK, HIGH); digitalWrite(RRST, HIGH); digitalWrite(OE, HIGH); digitalWrite(RCLK, HIGH); digitalWrite(RRST, LOW ); digitalWrite(OE, HIGH); digitalWrite(RCLK, LOW ); digitalWrite(RRST, LOW ); digitalWrite(OE, HIGH); digitalWrite(RCLK, HIGH); digitalWrite(RRST, LOW ); digitalWrite(OE, HIGH); digitalWrite(RCLK, LOW ); digitalWrite(RRST, LOW ); digitalWrite(OE, HIGH); digitalWrite(RCLK, LOW ); digitalWrite(RRST, HIGH); digitalWrite(OE, HIGH); digitalWrite(RCLK, HIGH); digitalWrite(RRST, HIGH); digitalWrite(OE, HIGH); digitalWrite(RCLK, HIGH); digitalWrite(RRST, HIGH); digitalWrite(OE, LOW ); digitalWrite(RCLK, LOW ); digitalWrite(RRST, HIGH); digitalWrite(OE, LOW ); digitalWrite(RCLK, HIGH); digitalWrite(RRST, HIGH); digitalWrite(OE, LOW ); } void readFifo() { const int HSIZE = 160; const int VSIZE = 120; const int DATABYTE = 2; int d0, d1, d2, d3, d4, d5, d6, d7; byte data; for (int v = 0; v < VSIZE; v++) { for (int h = 0; h < HSIZE; h++) { for (int d = 0; d < DATABYTE; d++) { digitalWrite(RCLK, HIGH); d0 = digitalRead(D0); d1 = digitalRead(D1); d2 = digitalRead(D2); d3 = digitalRead(D3); d4 = digitalRead(D4); d5 = digitalRead(D5); d6 = digitalRead(D6); d7 = digitalRead(D7); data = pinLevel2Byte(d0, 0) + pinLevel2Byte(d1, 1) + pinLevel2Byte(d2, 2) + pinLevel2Byte(d3, 3) + pinLevel2Byte(d4, 4) + pinLevel2Byte(d5, 5) + pinLevel2Byte(d6, 6) + pinLevel2Byte(d7, 7); digitalWrite(RCLK, LOW); char temp[3]; snprintf(temp, sizeof(temp), "%02X", data); Serial.print(temp); } } Serial.println(""); } // read end digitalWrite(RCLK, LOW ); digitalWrite(OE, HIGH); digitalWrite(RCLK, HIGH); digitalWrite(OE, HIGH); } byte pinLevel2Byte(int level, int shift) { byte result = (level == HIGH) ? 1 : 0; return result << shift; } void writeRegister(int address, int data) { const int I2C_WRITE_ADDR = 0x42; Wire.beginTransmission(I2C_WRITE_ADDR >> 1); Wire.write(address); Wire.write(data); Wire.endTransmission(); } int readRegister(int address) { const int I2C_READ_ADDR = 0x43; Wire.beginTransmission(I2C_READ_ADDR >> 1); Wire.write(address); Wire.endTransmission(); Wire.requestFrom(I2C_READ_ADDR >> 1, 1); return Wire.read(); } void resetCamera() { writeRegister(0x12, 0x80); delay(200); } void initRegister() { // QQVGA, RGB444 writeRegister(REG_CLKRC, 0x80); writeRegister(REG_COM11, 0x0A); writeRegister(REG_TSLB, 0x04); writeRegister(REG_COM7, 0x04); writeRegister(REG_RGB444, 0x02); writeRegister(REG_COM15, 0xD0); writeRegister(REG_HSTART, 0x16); writeRegister(REG_HSTOP, 0x04); writeRegister(REG_HREF, 0x24); writeRegister(REG_VSTART, 0x02); writeRegister(REG_VSTOP, 0x7A); writeRegister(REG_VREF, 0x0A); writeRegister(REG_COM10, 0x02); writeRegister(REG_COM3, 0x04); writeRegister(REG_COM14, 0x1A); writeRegister(0x72, 0x22); writeRegister(0x73, 0xF2); // COLOR SETTING writeRegister(0x4F, 0x80); writeRegister(0x50, 0x80); writeRegister(0x51, 0x00); writeRegister(0x52, 0x22); writeRegister(0x53, 0x5E); writeRegister(0x54, 0x80); writeRegister(0x56, 0x40); writeRegister(0x58, 0x9E); writeRegister(0x59, 0x88); writeRegister(0x5A, 0x88); writeRegister(0x5B, 0x44); writeRegister(0x5C, 0x67); writeRegister(0x5D, 0x49); writeRegister(0x5E, 0x0E); writeRegister(0x69, 0x00); writeRegister(0x6A, 0x40); writeRegister(0x6B, 0x0A); writeRegister(0x6C, 0x0A); writeRegister(0x6D, 0x55); writeRegister(0x6E, 0x11); writeRegister(0x6F, 0x9F); writeRegister(0xB0, 0x84); } void printData(int address, int data) { char temp[16]; snprintf(temp, sizeof(temp), "addr=%02X data=%02X", address, data); Serial.println(temp); } void receiveCommand(char *buff) { char c = '\0'; while (c != '\r' ) { if (Serial.available() > 0) { c = Serial.read(); Serial.print(c); *buff++ = c; } } *buff = '\0'; Serial.println(""); } void setup() { Serial.begin(115200); Wire.begin(); printMenu(); setPinMode(); resetCamera(); initRegister(); } void loop() { int address = 0; int data = 0; char buff[256]; char *buff_ptr; char *cmd_ptr; char *arg1_ptr; char *arg2_ptr; const char DELIMITTER[] = " \t"; Serial.print("> "); receiveCommand(buff); // trim white space buff_ptr = buff; while ( (*buff_ptr == ' ') || (*buff_ptr == '\t') || (*buff_ptr == '\n') ) { buff_ptr++; } cmd_ptr = strtok(buff_ptr, DELIMITTER); switch (tolower(*cmd_ptr)) { case 'w': arg1_ptr = strtok(NULL, DELIMITTER); arg2_ptr = strtok(NULL, DELIMITTER); if ( (arg1_ptr != NULL) && (arg2_ptr != NULL) ) { address = hex2dec(arg1_ptr); data = hex2dec(arg2_ptr); printData(address, data); writeRegister(address, data); delay(10); } else { Serial.println("Syntax Error"); } break; case 'r': arg1_ptr = strtok(NULL, DELIMITTER); if (arg1_ptr != NULL) { address = hex2dec(arg1_ptr); data = readRegister(address); printData(address, data); delay(50); } else { Serial.println("Syntax Error"); } break; case 'd': Serial.println("Dump"); initFifo(); readFifo(); break; case 's': Serial.println("Start"); isWriteEnable = true; break; case 'h': printHelp(); break; default: Serial.println("Syntax Error"); break; } Serial.println(""); } void printMenu() { Serial.println("------------------------------"); Serial.println(" OV7670 Camera Debugger"); Serial.println("------------------------------"); Serial.println(""); } void printHelp() { Serial.println("WRITE :w [address] [data]"); Serial.println("READ :r [address]"); Serial.println("START :s"); Serial.println("DUMP :d"); Serial.println("HELP :h"); } int hex2dec(char *str) { return strtol(str, NULL, 16); }
次にov7670reg.h
#define REG_GAIN 0x00 /* Gain lower 8 bits (rest in vref) */ #define REG_BLUE 0x01 /* blue gain */ #define REG_RED 0x02 /* red gain */ #define REG_VREF 0x03 /* Pieces of GAIN, VSTART, VSTOP */ #define REG_COM1 0x04 /* Control 1 */ #define COM1_CCIR656 0x40 /* CCIR656 enable */ #define REG_BAVE 0x05 /* U/B Average level */ #define REG_GbAVE 0x06 /* Y/Gb Average level */ #define REG_AECHH 0x07 /* AEC MS 5 bits */ #define REG_RAVE 0x08 /* V/R Average level */ #define REG_COM2 0x09 /* Control 2 */ #define COM2_SSLEEP 0x10 /* Soft sleep mode */ #define REG_PID 0x0a /* Product ID MSB */ #define REG_VER 0x0b /* Product ID LSB */ #define REG_COM3 0x0c /* Control 3 */ #define COM3_SWAP 0x40 /* Byte swap */ #define COM3_SCALEEN 0x08 /* Enable scaling */ #define COM3_DCWEN 0x04 /* Enable downsamp/crop/window */ #define REG_COM4 0x0d /* Control 4 */ #define REG_COM5 0x0e /* All "reserved" */ #define REG_COM6 0x0f /* Control 6 */ #define REG_AECH 0x10 /* More bits of AEC value */ #define REG_CLKRC 0x11 /* Clocl control */ #define CLK_EXT 0x40 /* Use external clock directly */ #define CLK_SCALE 0x3f /* Mask for internal clock scale */ #define REG_COM7 0x12 /* Control 7 */ #define COM7_RESET 0x80 /* Register reset */ #define COM7_FMT_MASK 0x38 #define COM7_FMT_VGA 0x00 #define COM7_FMT_CIF 0x20 /* CIF format */ #define COM7_FMT_QVGA 0x10 /* QVGA format */ #define COM7_FMT_QCIF 0x08 /* QCIF format */ #define COM7_RGB 0x04 /* bits 0 and 2 - RGB format */ #define COM7_YUV 0x00 /* YUV */ #define COM7_BAYER 0x01 /* Bayer format */ #define COM7_PBAYER 0x05 /* "Processed bayer" */ #define REG_COM8 0x13 /* Control 8 */ #define COM8_FASTAEC 0x80 /* Enable fast AGC/AEC */ #define COM8_AECSTEP 0x40 /* Unlimited AEC step size */ #define COM8_BFILT 0x20 /* Band filter enable */ #define COM8_AGC 0x04 /* Auto gain enable */ #define COM8_AWB 0x02 /* White balance enable */ #define COM8_AEC 0x01 /* Auto exposure enable */ #define REG_COM9 0x14 /* Control 9 - gain ceiling */ #define REG_COM10 0x15 /* Control 10 */ #define COM10_HSYNC 0x40 /* HSYNC instead of HREF */ #define COM10_PCLK_HB 0x20 /* Suppress PCLK on horiz blank */ #define COM10_HREF_REV 0x08 /* Reverse HREF */ #define COM10_VS_LEAD 0x04 /* VSYNC on clock leading edge */ #define COM10_VS_NEG 0x02 /* VSYNC negative */ #define COM10_HS_NEG 0x01 /* HSYNC negative */ #define REG_HSTART 0x17 /* Horiz start high bits */ #define REG_HSTOP 0x18 /* Horiz stop high bits */ #define REG_VSTART 0x19 /* Vert start high bits */ #define REG_VSTOP 0x1a /* Vert stop high bits */ #define REG_PSHFT 0x1b /* Pixel delay after HREF */ #define REG_MIDH 0x1c /* Manuf. ID high */ #define REG_MIDL 0x1d /* Manuf. ID low */ #define REG_MVFP 0x1e /* Mirror / vflip */ #define MVFP_MIRROR 0x20 /* Mirror image */ #define MVFP_FLIP 0x10 /* Vertical flip */ #define REG_AEW 0x24 /* AGC upper limit */ #define REG_AEB 0x25 /* AGC lower limit */ #define REG_VPT 0x26 /* AGC/AEC fast mode op region */ #define REG_HSYST 0x30 /* HSYNC rising edge delay */ #define REG_HSYEN 0x31 /* HSYNC falling edge delay */ #define REG_HREF 0x32 /* HREF pieces */ #define REG_TSLB 0x3a /* lots of stuff */ #define TSLB_YLAST 0x04 /* UYVY or VYUY - see com13 */ #define REG_COM11 0x3b /* Control 11 */ #define COM11_NIGHT 0x80 /* NIght mode enable */ #define COM11_NMFR 0x60 /* Two bit NM frame rate */ #define COM11_HZAUTO 0x10 /* Auto detect 50/60 Hz */ #define COM11_50HZ 0x08 /* Manual 50Hz select */ #define COM11_EXP 0x02 #define REG_COM12 0x3c /* Control 12 */ #define COM12_HREF 0x80 /* HREF always */ #define REG_COM13 0x3d /* Control 13 */ #define COM13_GAMMA 0x80 /* Gamma enable */ #define COM13_UVSAT 0x40 /* UV saturation auto adjustment */ #define COM13_UVSWAP 0x01 /* V before U - w/TSLB */ #define REG_COM14 0x3e /* Control 14 */ #define COM14_DCWEN 0x10 /* DCW/PCLK-scale enable */ #define REG_EDGE 0x3f /* Edge enhancement factor */ #define REG_COM15 0x40 /* Control 15 */ #define COM15_R10F0 0x00 /* Data range 10 to F0 */ #define COM15_R01FE 0x80 /* 01 to FE */ #define COM15_R00FF 0xc0 /* 00 to FF */ #define COM15_RGB565 0x10 /* RGB565 output */ #define COM15_RGB555 0x30 /* RGB555 output */ #define REG_COM16 0x41 /* Control 16 */ #define COM16_AWBGAIN 0x08 /* AWB gain enable */ #define REG_COM17 0x42 /* Control 17 */ #define COM17_AECWIN 0xc0 /* AEC window - must match COM4 */ #define COM17_CBAR 0x08 /* DSP Color bar */ #define REG_CMATRIX_BASE 0x4f #define CMATRIX_LEN 6 #define REG_CMATRIX_SIGN 0x58 #define REG_BRIGHT 0x55 /* Brightness */ #define REG_CONTRAS 0x56 /* Contrast control */ #define REG_GFIX 0x69 /* Fix gain control */ #define REG_REG76 0x76 /* OV's name */ #define R76_BLKPCOR 0x80 /* Black pixel correction enable */ #define R76_WHTPCOR 0x40 /* White pixel correction enable */ #define REG_RGB444 0x8c /* RGB 444 control */ #define R444_ENABLE 0x02 /* Turn on RGB444, overrides 5x5 */ #define R444_RGBX 0x01 /* Empty nibble at end */ #define REG_HAECC1 0x9f /* Hist AEC/AGC control 1 */ #define REG_HAECC2 0xa0 /* Hist AEC/AGC control 2 */ #define REG_BD50MAX 0xa5 /* 50hz banding step limit */ #define REG_HAECC3 0xa6 /* Hist AEC/AGC control 3 */ #define REG_HAECC4 0xa7 /* Hist AEC/AGC control 4 */ #define REG_HAECC5 0xa8 /* Hist AEC/AGC control 5 */ #define REG_HAECC6 0xa9 /* Hist AEC/AGC control 6 */ #define REG_HAECC7 0xaa /* Hist AEC/AGC control 7 */ #define REG_BD60MAX 0xab /* 60hz banding step limit */
これをArduinoに書き込めばOK.
いろいろwebで調べて,とりあえずQQVGAのRGB444で画像取得する例がいくつか見つかったので,それで試してる.
使い方は,シリアルモニタを開いて,
sを入力すると撮影,dで画像データのダンプが行われる.hで簡単なヘルプ(というほどのものでもないが)が表示される.なので,まぁスケッチを見ながらいろいろ試してみてくださいな.
画像データは,QQVGAのRGB444で動かしているので,ダンプすると160x120=19200画素のデータが,1画素あたり2バイト出力される.なので,トータルとしては38400バイトになる.
データの並びは,xBGRで出力される.なお,160画素ごとに改行を入れて出力される.改行を除去すると,1バイトあたり2文字でダンプされるので,結果として,76800文字のデータが得られることになる.
さて,データだけあってもつまらないので,Processingで画像を表示してみる.
Processingのコードは,こんな感じ.
int width = 160; int height = 120; String[] pixeldata; void setup() { frameRate(1); size(width, height); noStroke(); pixeldata = loadStrings("captureData.txt"); } void draw() { for (int i = 0; i < width * height; i++) { int x = i % width; int y = i / width; fill( unhex(String.valueOf(pixeldata[0].charAt(4*i+3))) *16, unhex(String.valueOf(pixeldata[0].charAt(4*i+2))) *16, unhex(String.valueOf(pixeldata[0].charAt(4*i+1))) *16 ); rect(x, y, 1, 1); } }
ダンプしたデータは,captureData.txtとしてProcessingのコードと同じディレクトリに保存してくださいな.
で,表示されるサンプル画像はこんな感じ.
ま,QQVGなのと,RGB各色4ビットしかないので,画質はそれなりですな.
あと,水平方向の位相がちょっとずれてるようなのと,電源投入一発目の画像がおかしいことがあるんだけど...
ま,お試しあれ.
参考サイト
いっぱいあって,いっぱい参考にした.特に以下の2つを参考にしたけど,ほかにもGoogleでov7670で検索してヒットするサイトは軒並みチェックした.先人に感謝.
トラ技 頒布カメラB(OV7670 FIFO AL422B)の動作確認 | mbed
HR2_blog: mbedでトラ技3月号の頒布カメラBを使う
2014.12.15追記
サンプル画像の元データ(captureData.txt)はこんな感じ.captureData.txt.zip.jpg
So-netのブログはテキストファイルのアップロードができないので,ZIP圧縮して,拡張子に.jpgをつけて無理矢理アップロードしてる.なので,ダウンロードしたら.jpgを消してZIPファイルとして展開してくださいな.
ArduinoでCMOSカメラを動かしてみる [Arduino]
前回から,ずいぶんと時間が経ってしまったが,ArduinoとCMOSカメラを接続して動かす続きをやってみる.
前回は,I2C(SCCB)で通信するところで終わっていたが,今回は画像の取得をやってみる.CMOSカメラで撮影した画像データをFIFOに保存し,その保存されたデータを読み出してみる.
というわけで,まずはArduinoとCMOSカメラを接続するわけだが,前回と同様の問題にぶち当たる.Aruduinoは5Vで動いていて,CMOSカメラのFIFOは3.3V,ov7670は2.8Vで動いてて,電圧が違う.直結していいかどうか確認が必要だ.
で,データシートを調べてみた.
今回のCMOSカメラで使われているFIFOは,AL422というデバイスで,AL422へのIO入力は5.5VまでOKのようだ(3.3V駆動時).なので,ArduinoからFIFOへ出力する場合の接続は直結でも問題ない.
また,Arduinoからov7670へ出力するような接続は,前回のI2C以外では使わないので気にしなくていい.
逆にCMOSカメラ,すなわちFIFO,もしくはov7670の出力をArduinoで受ける場合だと,2.8Vもしくは3.3V出力を5Vで受けるので,High/Loが正しく検出できない.実力的には問題が出ないかもしれないが仕様上はアウトだ.
というわけで,Arduino-->CMOSカメラは気にせず直結,CMOSカメラ-->Arduinoはレベル変換ICを間に挟むことにする.
今回使用するレベル変換ICは,秋月電子通商の「8ビット双方向ロジックレベル変換モジュール」だ.これを2個使用した.
では,回路図を示そう.前回同様,fritzingで書いてみた.
で,実際接続してみたのがこちら
配線数が多いのでさすがにごちゃごちゃしてる...
とりあえずこれで接続は完了.
あとはスケッチの作成だが,それはまた次回.
前回は,I2C(SCCB)で通信するところで終わっていたが,今回は画像の取得をやってみる.CMOSカメラで撮影した画像データをFIFOに保存し,その保存されたデータを読み出してみる.
というわけで,まずはArduinoとCMOSカメラを接続するわけだが,前回と同様の問題にぶち当たる.Aruduinoは5Vで動いていて,CMOSカメラのFIFOは3.3V,ov7670は2.8Vで動いてて,電圧が違う.直結していいかどうか確認が必要だ.
で,データシートを調べてみた.
今回のCMOSカメラで使われているFIFOは,AL422というデバイスで,AL422へのIO入力は5.5VまでOKのようだ(3.3V駆動時).なので,ArduinoからFIFOへ出力する場合の接続は直結でも問題ない.
また,Arduinoからov7670へ出力するような接続は,前回のI2C以外では使わないので気にしなくていい.
逆にCMOSカメラ,すなわちFIFO,もしくはov7670の出力をArduinoで受ける場合だと,2.8Vもしくは3.3V出力を5Vで受けるので,High/Loが正しく検出できない.実力的には問題が出ないかもしれないが仕様上はアウトだ.
というわけで,Arduino-->CMOSカメラは気にせず直結,CMOSカメラ-->Arduinoはレベル変換ICを間に挟むことにする.
今回使用するレベル変換ICは,秋月電子通商の「8ビット双方向ロジックレベル変換モジュール」だ.これを2個使用した.
では,回路図を示そう.前回同様,fritzingで書いてみた.
で,実際接続してみたのがこちら
配線数が多いのでさすがにごちゃごちゃしてる...
とりあえずこれで接続は完了.
あとはスケッチの作成だが,それはまた次回.
ArduinoでCMOSカメラと通信してみる [Arduino]
前回,ArduinoとCMOSカメラを接続しようと思ったのだが,電圧があわなくて接続できず動作させるとことができなかった.
というわけで,レベル変換ICだが,今回は,秋月電子通商の「I2Cバス用双方向電圧レベル変換モジュール(PCA9306)」を購入してみた.
で,こいつの電源だが,Aruduino側は5VなのでArduino側からもらってくればいいんだけど,OV7670側のI2C(厳密にはSCCB)は2.8Vだ.2.8VなんてArduino側にはない.
どうしようか迷ったが,CMOSカメラモジュール内で2.8Vを作ってるので,そこから引き出すことにした.回路図と,CMOSカメラモジュールを見比べながら,2.8Vが引き出せそうなところを探した.
で,下の写真はレンズを外したCMOSカメラモジュールで,赤矢印のところから2.8Vが引き出せる.ここに線材を半田づけして引き出した.
ここまでくれば,あとはCMOSカメラとArduinoを接続するだけだ.とりあえず通信できるかどうか確認するために,I2C(SCCB)に必要なところだけつないでみる.
回路図をfritzingで書いてみた.使いこなせてない&回路図書くことなんて滅多にないので,図が微妙かもしれないが.
で,つないでみた.
あとは,実際に通信してみるのだが,スケッチは,トランジスタ技術SPECIALの「カメラ・モジュールの動かし方と応用製作」にのってるのを使わせてもらった.詳細は本を参照してくださいな.
で,とりあえず,OV7670のレジスタマップを見ながらアドレス0AのProduct IDを読み出してみたら,ちゃんと76が返ってきた.ほかにもレジスタを読み出してみたが,どうやら通信はうまくできているようだ.
よし,次は,画像の取得だ.
ちなみに,画像データはFIFOモジュールと通信することになるのだが,こっちはFIFOの電源電圧が3.3Vだ.ここでもまたレベル変換ICが必要になる.
これについてはまた次回.
というわけで,レベル変換ICだが,今回は,秋月電子通商の「I2Cバス用双方向電圧レベル変換モジュール(PCA9306)」を購入してみた.
で,こいつの電源だが,Aruduino側は5VなのでArduino側からもらってくればいいんだけど,OV7670側のI2C(厳密にはSCCB)は2.8Vだ.2.8VなんてArduino側にはない.
どうしようか迷ったが,CMOSカメラモジュール内で2.8Vを作ってるので,そこから引き出すことにした.回路図と,CMOSカメラモジュールを見比べながら,2.8Vが引き出せそうなところを探した.
で,下の写真はレンズを外したCMOSカメラモジュールで,赤矢印のところから2.8Vが引き出せる.ここに線材を半田づけして引き出した.
ここまでくれば,あとはCMOSカメラとArduinoを接続するだけだ.とりあえず通信できるかどうか確認するために,I2C(SCCB)に必要なところだけつないでみる.
回路図をfritzingで書いてみた.使いこなせてない&回路図書くことなんて滅多にないので,図が微妙かもしれないが.
で,つないでみた.
あとは,実際に通信してみるのだが,スケッチは,トランジスタ技術SPECIALの「カメラ・モジュールの動かし方と応用製作」にのってるのを使わせてもらった.詳細は本を参照してくださいな.
で,とりあえず,OV7670のレジスタマップを見ながらアドレス0AのProduct IDを読み出してみたら,ちゃんと76が返ってきた.ほかにもレジスタを読み出してみたが,どうやら通信はうまくできているようだ.
よし,次は,画像の取得だ.
ちなみに,画像データはFIFOモジュールと通信することになるのだが,こっちはFIFOの電源電圧が3.3Vだ.ここでもまたレベル変換ICが必要になる.
これについてはまた次回.